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, 2012 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[][42] = {
\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, OPT_Grid },
\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[70000], *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 static char buf[MSG_SIZ];
\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
393 if(!barbaric) return s;
\r
394 if(!s) return ""; // sanity
\r
395 while(english[i]) {
\r
396 if(!strcmp(s, english[i])) return foreign[i];
\r
397 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
398 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
407 Translate(HWND hDlg, int dialogID)
\r
408 { // translate all text items in the given dialog
\r
410 char buf[MSG_SIZ], *s;
\r
411 if(!barbaric) return;
\r
412 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
413 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
414 GetWindowText( hDlg, buf, MSG_SIZ );
\r
416 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
417 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
418 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
419 if(strlen(buf) == 0) continue;
\r
421 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
426 TranslateOneMenu(int i, HMENU subMenu)
\r
429 static MENUITEMINFO info;
\r
431 info.cbSize = sizeof(MENUITEMINFO);
\r
432 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
433 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
435 info.dwTypeData = buf;
\r
436 info.cch = sizeof(buf);
\r
437 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
439 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
440 else menuText[i][j] = strdup(buf); // remember original on first change
\r
442 if(buf[0] == NULLCHAR) continue;
\r
443 info.dwTypeData = T_(buf);
\r
444 info.cch = strlen(buf)+1;
\r
445 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
451 TranslateMenus(int addLanguage)
\r
454 WIN32_FIND_DATA fileData;
\r
456 #define IDM_English 1970
\r
458 HMENU mainMenu = GetMenu(hwndMain);
\r
459 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
460 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
461 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
462 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
463 TranslateOneMenu(i, subMenu);
\r
465 DrawMenuBar(hwndMain);
\r
468 if(!addLanguage) return;
\r
469 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
470 HMENU mainMenu = GetMenu(hwndMain);
\r
471 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
472 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
473 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
474 i = 0; lastChecked = IDM_English;
\r
476 char *p, *q = fileData.cFileName;
\r
477 int checkFlag = MF_UNCHECKED;
\r
478 languageFile[i] = strdup(q);
\r
479 if(barbaric && !strcmp(oldLanguage, q)) {
\r
480 checkFlag = MF_CHECKED;
\r
481 lastChecked = IDM_English + i + 1;
\r
482 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
484 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
485 p = strstr(fileData.cFileName, ".lng");
\r
487 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
488 } while(FindNextFile(hFind, &fileData));
\r
495 #define IDM_RecentEngines 3000
\r
498 RecentEngineMenu (char *s)
\r
500 if(appData.icsActive) return;
\r
501 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
502 HMENU mainMenu = GetMenu(hwndMain);
\r
503 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
504 int i=IDM_RecentEngines;
\r
505 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
506 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
508 char *p = strchr(s, '\n');
\r
509 if(p == NULL) return; // malformed!
\r
511 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
525 int cliWidth, cliHeight;
\r
528 SizeInfo sizeInfo[] =
\r
530 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
531 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
532 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
533 { "petite", 33, 1, 1, 1, 0, 0 },
\r
534 { "slim", 37, 2, 1, 0, 0, 0 },
\r
535 { "small", 40, 2, 1, 0, 0, 0 },
\r
536 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
537 { "middling", 49, 2, 0, 0, 0, 0 },
\r
538 { "average", 54, 2, 0, 0, 0, 0 },
\r
539 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
540 { "medium", 64, 3, 0, 0, 0, 0 },
\r
541 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
542 { "large", 80, 3, 0, 0, 0, 0 },
\r
543 { "big", 87, 3, 0, 0, 0, 0 },
\r
544 { "huge", 95, 3, 0, 0, 0, 0 },
\r
545 { "giant", 108, 3, 0, 0, 0, 0 },
\r
546 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
547 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
548 { NULL, 0, 0, 0, 0, 0, 0 }
\r
551 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
552 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
554 { 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
555 { 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
556 { 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
557 { 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
558 { 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
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
574 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
583 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
584 #define N_BUTTONS 5
\r
586 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
588 {"<<", IDM_ToStart, NULL, NULL},
\r
589 {"<", IDM_Backward, NULL, NULL},
\r
590 {"P", IDM_Pause, NULL, NULL},
\r
591 {">", IDM_Forward, NULL, NULL},
\r
592 {">>", IDM_ToEnd, NULL, NULL},
\r
595 int tinyLayout = 0, smallLayout = 0;
\r
596 #define MENU_BAR_ITEMS 9
\r
597 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
598 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
599 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
603 MySound sounds[(int)NSoundClasses];
\r
604 MyTextAttribs textAttribs[(int)NColorClasses];
\r
606 MyColorizeAttribs colorizeAttribs[] = {
\r
607 { (COLORREF)0, 0, N_("Shout Text") },
\r
608 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
609 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
610 { (COLORREF)0, 0, N_("Channel Text") },
\r
611 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
612 { (COLORREF)0, 0, N_("Tell Text") },
\r
613 { (COLORREF)0, 0, N_("Challenge Text") },
\r
614 { (COLORREF)0, 0, N_("Request Text") },
\r
615 { (COLORREF)0, 0, N_("Seek Text") },
\r
616 { (COLORREF)0, 0, N_("Normal Text") },
\r
617 { (COLORREF)0, 0, N_("None") }
\r
622 static char *commentTitle;
\r
623 static char *commentText;
\r
624 static int commentIndex;
\r
625 static Boolean editComment = FALSE;
\r
628 char errorTitle[MSG_SIZ];
\r
629 char errorMessage[2*MSG_SIZ];
\r
630 HWND errorDialog = NULL;
\r
631 BOOLEAN moveErrorMessageUp = FALSE;
\r
632 BOOLEAN consoleEcho = TRUE;
\r
633 CHARFORMAT consoleCF;
\r
634 COLORREF consoleBackgroundColor;
\r
636 char *programVersion;
\r
642 typedef int CPKind;
\r
651 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
654 #define INPUT_SOURCE_BUF_SIZE 4096
\r
656 typedef struct _InputSource {
\r
663 char buf[INPUT_SOURCE_BUF_SIZE];
\r
667 InputCallback func;
\r
668 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
672 InputSource *consoleInputSource;
\r
677 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
678 VOID ConsoleCreate();
\r
680 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
681 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
682 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
683 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
685 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
686 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
687 void ParseIcsTextMenu(char *icsTextMenuString);
\r
688 VOID PopUpNameDialog(char firstchar);
\r
689 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
693 int GameListOptions();
\r
695 int dummy; // [HGM] for obsolete args
\r
697 HWND hwndMain = NULL; /* root window*/
\r
698 HWND hwndConsole = NULL;
\r
699 HWND commentDialog = NULL;
\r
700 HWND moveHistoryDialog = NULL;
\r
701 HWND evalGraphDialog = NULL;
\r
702 HWND engineOutputDialog = NULL;
\r
703 HWND gameListDialog = NULL;
\r
704 HWND editTagsDialog = NULL;
\r
706 int commentUp = FALSE;
\r
708 WindowPlacement wpMain;
\r
709 WindowPlacement wpConsole;
\r
710 WindowPlacement wpComment;
\r
711 WindowPlacement wpMoveHistory;
\r
712 WindowPlacement wpEvalGraph;
\r
713 WindowPlacement wpEngineOutput;
\r
714 WindowPlacement wpGameList;
\r
715 WindowPlacement wpTags;
\r
717 VOID EngineOptionsPopup(); // [HGM] settings
\r
719 VOID GothicPopUp(char *title, VariantClass variant);
\r
721 * Setting "frozen" should disable all user input other than deleting
\r
722 * the window. We do this while engines are initializing themselves.
\r
724 static int frozen = 0;
\r
725 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
731 if (frozen) return;
\r
733 hmenu = GetMenu(hwndMain);
\r
734 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
735 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
737 DrawMenuBar(hwndMain);
\r
740 /* Undo a FreezeUI */
\r
746 if (!frozen) return;
\r
748 hmenu = GetMenu(hwndMain);
\r
749 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
750 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
752 DrawMenuBar(hwndMain);
\r
755 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
757 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
763 #define JAWS_ALT_INTERCEPT
\r
764 #define JAWS_KBUP_NAVIGATION
\r
765 #define JAWS_KBDOWN_NAVIGATION
\r
766 #define JAWS_MENU_ITEMS
\r
767 #define JAWS_SILENCE
\r
768 #define JAWS_REPLAY
\r
770 #define JAWS_COPYRIGHT
\r
771 #define JAWS_DELETE(X) X
\r
772 #define SAYMACHINEMOVE()
\r
776 /*---------------------------------------------------------------------------*\
\r
780 \*---------------------------------------------------------------------------*/
\r
783 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
784 LPSTR lpCmdLine, int nCmdShow)
\r
787 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
788 // INITCOMMONCONTROLSEX ex;
\r
792 LoadLibrary("RICHED32.DLL");
\r
793 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
795 if (!InitApplication(hInstance)) {
\r
798 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
805 // InitCommonControlsEx(&ex);
\r
806 InitCommonControls();
\r
808 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
809 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
810 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
812 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
814 while (GetMessage(&msg, /* message structure */
\r
815 NULL, /* handle of window receiving the message */
\r
816 0, /* lowest message to examine */
\r
817 0)) /* highest message to examine */
\r
820 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
821 // [HGM] navigate: switch between all windows with tab
\r
822 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
823 int i, currentElement = 0;
\r
825 // first determine what element of the chain we come from (if any)
\r
826 if(appData.icsActive) {
\r
827 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
828 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
830 if(engineOutputDialog && EngineOutputIsUp()) {
\r
831 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
832 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
834 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
835 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
837 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
838 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
839 if(msg.hwnd == e1) currentElement = 2; else
\r
840 if(msg.hwnd == e2) currentElement = 3; else
\r
841 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
842 if(msg.hwnd == mh) currentElement = 4; else
\r
843 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
844 if(msg.hwnd == hText) currentElement = 5; else
\r
845 if(msg.hwnd == hInput) currentElement = 6; else
\r
846 for (i = 0; i < N_BUTTONS; i++) {
\r
847 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
850 // determine where to go to
\r
851 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
853 currentElement = (currentElement + direction) % 7;
\r
854 switch(currentElement) {
\r
856 h = hwndMain; break; // passing this case always makes the loop exit
\r
858 h = buttonDesc[0].hwnd; break; // could be NULL
\r
860 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
863 if(!EngineOutputIsUp()) continue;
\r
866 if(!MoveHistoryIsUp()) continue;
\r
868 // case 6: // input to eval graph does not seem to get here!
\r
869 // if(!EvalGraphIsUp()) continue;
\r
870 // h = evalGraphDialog; break;
\r
872 if(!appData.icsActive) continue;
\r
876 if(!appData.icsActive) continue;
\r
882 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
883 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
886 continue; // this message now has been processed
\r
890 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
891 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
892 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
893 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
894 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
895 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
896 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
897 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
898 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
899 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
900 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
901 for(i=0; i<MAX_CHAT; i++)
\r
902 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
905 if(done) continue; // [HGM] chat: end patch
\r
906 TranslateMessage(&msg); /* Translates virtual key codes */
\r
907 DispatchMessage(&msg); /* Dispatches message to window */
\r
912 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
915 /*---------------------------------------------------------------------------*\
\r
917 * Initialization functions
\r
919 \*---------------------------------------------------------------------------*/
\r
923 { // update user logo if necessary
\r
924 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
926 if(appData.autoLogo) {
\r
927 curName = UserName();
\r
928 if(strcmp(curName, oldUserName)) {
\r
929 GetCurrentDirectory(MSG_SIZ, dir);
\r
930 SetCurrentDirectory(installDir);
\r
931 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
932 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
933 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
934 if(userLogo == NULL)
\r
935 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
936 SetCurrentDirectory(dir); /* return to prev directory */
\r
942 InitApplication(HINSTANCE hInstance)
\r
946 /* Fill in window class structure with parameters that describe the */
\r
949 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
950 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
951 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
952 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
953 wc.hInstance = hInstance; /* Owner of this class */
\r
954 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
955 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
956 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
957 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
958 wc.lpszClassName = szAppName; /* Name to register as */
\r
960 /* Register the window class and return success/failure code. */
\r
961 if (!RegisterClass(&wc)) return FALSE;
\r
963 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
964 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
966 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
967 wc.hInstance = hInstance;
\r
968 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
969 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
970 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
971 wc.lpszMenuName = NULL;
\r
972 wc.lpszClassName = szConsoleName;
\r
974 if (!RegisterClass(&wc)) return FALSE;
\r
979 /* Set by InitInstance, used by EnsureOnScreen */
\r
980 int screenHeight, screenWidth;
\r
983 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
985 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
986 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
987 if (*x > screenWidth - 32) *x = 0;
\r
988 if (*y > screenHeight - 32) *y = 0;
\r
989 if (*x < minX) *x = minX;
\r
990 if (*y < minY) *y = minY;
\r
994 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
996 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
997 GetCurrentDirectory(MSG_SIZ, dir);
\r
998 SetCurrentDirectory(installDir);
\r
999 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1000 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1002 if (cps->programLogo == NULL && appData.debugMode) {
\r
1003 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1005 } else if(appData.autoLogo) {
\r
1006 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1007 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1008 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1010 if(appData.directory[n] && appData.directory[n][0]) {
\r
1011 SetCurrentDirectory(appData.directory[n]);
\r
1012 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1015 SetCurrentDirectory(dir); /* return to prev directory */
\r
1021 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1022 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1024 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1025 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1026 liteBackTextureMode = appData.liteBackTextureMode;
\r
1028 if (liteBackTexture == NULL && appData.debugMode) {
\r
1029 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1033 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1034 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1035 darkBackTextureMode = appData.darkBackTextureMode;
\r
1037 if (darkBackTexture == NULL && appData.debugMode) {
\r
1038 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1044 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1046 HWND hwnd; /* Main window handle. */
\r
1048 WINDOWPLACEMENT wp;
\r
1051 hInst = hInstance; /* Store instance handle in our global variable */
\r
1052 programName = szAppName;
\r
1054 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1055 *filepart = NULLCHAR;
\r
1057 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1059 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1060 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1061 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1062 /* xboard, and older WinBoards, controlled the move sound with the
\r
1063 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1064 always turn the option on (so that the backend will call us),
\r
1065 then let the user turn the sound off by setting it to silence if
\r
1066 desired. To accommodate old winboard.ini files saved by old
\r
1067 versions of WinBoard, we also turn off the sound if the option
\r
1068 was initially set to false. [HGM] taken out of InitAppData */
\r
1069 if (!appData.ringBellAfterMoves) {
\r
1070 sounds[(int)SoundMove].name = strdup("");
\r
1071 appData.ringBellAfterMoves = TRUE;
\r
1073 if (appData.debugMode) {
\r
1074 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1075 setbuf(debugFP, NULL);
\r
1078 LoadLanguageFile(appData.language);
\r
1082 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1083 // InitEngineUCI( installDir, &second );
\r
1085 /* Create a main window for this application instance. */
\r
1086 hwnd = CreateWindow(szAppName, szTitle,
\r
1087 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1088 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1089 NULL, NULL, hInstance, NULL);
\r
1092 /* If window could not be created, return "failure" */
\r
1097 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1098 LoadLogo(&first, 0, FALSE);
\r
1099 LoadLogo(&second, 1, appData.icsActive);
\r
1103 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1104 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1105 iconCurrent = iconWhite;
\r
1106 InitDrawingColors();
\r
1107 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1108 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1109 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1110 /* Compute window size for each board size, and use the largest
\r
1111 size that fits on this screen as the default. */
\r
1112 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1113 if (boardSize == (BoardSize)-1 &&
\r
1114 winH <= screenHeight
\r
1115 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1116 && winW <= screenWidth) {
\r
1117 boardSize = (BoardSize)ibs;
\r
1121 InitDrawingSizes(boardSize, 0);
\r
1122 RecentEngineMenu(appData.recentEngineList);
\r
1124 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1126 /* [AS] Load textures if specified */
\r
1129 mysrandom( (unsigned) time(NULL) );
\r
1131 /* [AS] Restore layout */
\r
1132 if( wpMoveHistory.visible ) {
\r
1133 MoveHistoryPopUp();
\r
1136 if( wpEvalGraph.visible ) {
\r
1140 if( wpEngineOutput.visible ) {
\r
1141 EngineOutputPopUp();
\r
1144 /* Make the window visible; update its client area; and return "success" */
\r
1145 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1146 wp.length = sizeof(WINDOWPLACEMENT);
\r
1148 wp.showCmd = nCmdShow;
\r
1149 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1150 wp.rcNormalPosition.left = wpMain.x;
\r
1151 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1152 wp.rcNormalPosition.top = wpMain.y;
\r
1153 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1154 SetWindowPlacement(hwndMain, &wp);
\r
1156 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1158 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1159 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1161 if (hwndConsole) {
\r
1163 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1164 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1166 ShowWindow(hwndConsole, nCmdShow);
\r
1167 SetActiveWindow(hwndConsole);
\r
1169 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1170 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1179 HMENU hmenu = GetMenu(hwndMain);
\r
1181 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1182 MF_BYCOMMAND|((appData.icsActive &&
\r
1183 *appData.icsCommPort != NULLCHAR) ?
\r
1184 MF_ENABLED : MF_GRAYED));
\r
1185 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1186 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1187 MF_CHECKED : MF_UNCHECKED));
\r
1190 //---------------------------------------------------------------------------------------------------------
\r
1192 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1193 #define XBOARD FALSE
\r
1195 #define OPTCHAR "/"
\r
1196 #define SEPCHAR "="
\r
1200 // front-end part of option handling
\r
1203 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1205 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1206 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1209 lf->lfEscapement = 0;
\r
1210 lf->lfOrientation = 0;
\r
1211 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1212 lf->lfItalic = mfp->italic;
\r
1213 lf->lfUnderline = mfp->underline;
\r
1214 lf->lfStrikeOut = mfp->strikeout;
\r
1215 lf->lfCharSet = mfp->charset;
\r
1216 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1217 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1218 lf->lfQuality = DEFAULT_QUALITY;
\r
1219 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1220 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1224 CreateFontInMF(MyFont *mf)
\r
1226 LFfromMFP(&mf->lf, &mf->mfp);
\r
1227 if (mf->hf) DeleteObject(mf->hf);
\r
1228 mf->hf = CreateFontIndirect(&mf->lf);
\r
1231 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1233 colorVariable[] = {
\r
1234 &whitePieceColor,
\r
1235 &blackPieceColor,
\r
1236 &lightSquareColor,
\r
1237 &darkSquareColor,
\r
1238 &highlightSquareColor,
\r
1239 &premoveHighlightColor,
\r
1241 &consoleBackgroundColor,
\r
1242 &appData.fontForeColorWhite,
\r
1243 &appData.fontBackColorWhite,
\r
1244 &appData.fontForeColorBlack,
\r
1245 &appData.fontBackColorBlack,
\r
1246 &appData.evalHistColorWhite,
\r
1247 &appData.evalHistColorBlack,
\r
1248 &appData.highlightArrowColor,
\r
1251 /* Command line font name parser. NULL name means do nothing.
\r
1252 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1253 For backward compatibility, syntax without the colon is also
\r
1254 accepted, but font names with digits in them won't work in that case.
\r
1257 ParseFontName(char *name, MyFontParams *mfp)
\r
1260 if (name == NULL) return;
\r
1262 q = strchr(p, ':');
\r
1264 if (q - p >= sizeof(mfp->faceName))
\r
1265 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1266 memcpy(mfp->faceName, p, q - p);
\r
1267 mfp->faceName[q - p] = NULLCHAR;
\r
1270 q = mfp->faceName;
\r
1271 while (*p && !isdigit(*p)) {
\r
1273 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1274 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1276 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1279 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1280 mfp->pointSize = (float) atof(p);
\r
1281 mfp->bold = (strchr(p, 'b') != NULL);
\r
1282 mfp->italic = (strchr(p, 'i') != NULL);
\r
1283 mfp->underline = (strchr(p, 'u') != NULL);
\r
1284 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1285 mfp->charset = DEFAULT_CHARSET;
\r
1286 q = strchr(p, 'c');
\r
1288 mfp->charset = (BYTE) atoi(q+1);
\r
1292 ParseFont(char *name, int number)
\r
1293 { // wrapper to shield back-end from 'font'
\r
1294 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1299 { // in WB we have a 2D array of fonts; this initializes their description
\r
1301 /* Point font array elements to structures and
\r
1302 parse default font names */
\r
1303 for (i=0; i<NUM_FONTS; i++) {
\r
1304 for (j=0; j<NUM_SIZES; j++) {
\r
1305 font[j][i] = &fontRec[j][i];
\r
1306 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1313 { // here we create the actual fonts from the selected descriptions
\r
1315 for (i=0; i<NUM_FONTS; i++) {
\r
1316 for (j=0; j<NUM_SIZES; j++) {
\r
1317 CreateFontInMF(font[j][i]);
\r
1321 /* Color name parser.
\r
1322 X version accepts X color names, but this one
\r
1323 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1325 ParseColorName(char *name)
\r
1327 int red, green, blue, count;
\r
1328 char buf[MSG_SIZ];
\r
1330 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1332 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1333 &red, &green, &blue);
\r
1336 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1337 DisplayError(buf, 0);
\r
1338 return RGB(0, 0, 0);
\r
1340 return PALETTERGB(red, green, blue);
\r
1344 ParseColor(int n, char *name)
\r
1345 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1346 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1350 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1352 char *e = argValue;
\r
1356 if (*e == 'b') eff |= CFE_BOLD;
\r
1357 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1358 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1359 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1360 else if (*e == '#' || isdigit(*e)) break;
\r
1364 *color = ParseColorName(e);
\r
1368 ParseTextAttribs(ColorClass cc, char *s)
\r
1369 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1370 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1371 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1375 ParseBoardSize(void *addr, char *name)
\r
1376 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1377 BoardSize bs = SizeTiny;
\r
1378 while (sizeInfo[bs].name != NULL) {
\r
1379 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1380 *(BoardSize *)addr = bs;
\r
1385 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1390 { // [HGM] import name from appData first
\r
1393 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1394 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1395 textAttribs[cc].sound.data = NULL;
\r
1396 MyLoadSound(&textAttribs[cc].sound);
\r
1398 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1399 textAttribs[cc].sound.name = strdup("");
\r
1400 textAttribs[cc].sound.data = NULL;
\r
1402 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1403 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1404 sounds[sc].data = NULL;
\r
1405 MyLoadSound(&sounds[sc]);
\r
1410 SetCommPortDefaults()
\r
1412 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1413 dcb.DCBlength = sizeof(DCB);
\r
1414 dcb.BaudRate = 9600;
\r
1415 dcb.fBinary = TRUE;
\r
1416 dcb.fParity = FALSE;
\r
1417 dcb.fOutxCtsFlow = FALSE;
\r
1418 dcb.fOutxDsrFlow = FALSE;
\r
1419 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1420 dcb.fDsrSensitivity = FALSE;
\r
1421 dcb.fTXContinueOnXoff = TRUE;
\r
1422 dcb.fOutX = FALSE;
\r
1424 dcb.fNull = FALSE;
\r
1425 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1426 dcb.fAbortOnError = FALSE;
\r
1428 dcb.Parity = SPACEPARITY;
\r
1429 dcb.StopBits = ONESTOPBIT;
\r
1432 // [HGM] args: these three cases taken out to stay in front-end
\r
1434 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1435 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1436 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1437 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1439 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1440 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1441 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1442 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1443 ad->argName, mfp->faceName, mfp->pointSize,
\r
1444 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1445 mfp->bold ? "b" : "",
\r
1446 mfp->italic ? "i" : "",
\r
1447 mfp->underline ? "u" : "",
\r
1448 mfp->strikeout ? "s" : "",
\r
1449 (int)mfp->charset);
\r
1455 { // [HGM] copy the names from the internal WB variables to appData
\r
1458 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1459 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1460 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1461 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1465 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1466 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1467 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1468 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1469 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1470 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1471 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1472 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1473 (ta->effects) ? " " : "",
\r
1474 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1478 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1479 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1480 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1481 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1482 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1486 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1487 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1488 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1492 ParseCommPortSettings(char *s)
\r
1493 { // wrapper to keep dcb from back-end
\r
1494 ParseCommSettings(s, &dcb);
\r
1499 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1500 GetActualPlacement(hwndMain, &wpMain);
\r
1501 GetActualPlacement(hwndConsole, &wpConsole);
\r
1502 GetActualPlacement(commentDialog, &wpComment);
\r
1503 GetActualPlacement(editTagsDialog, &wpTags);
\r
1504 GetActualPlacement(gameListDialog, &wpGameList);
\r
1505 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1506 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1507 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1511 PrintCommPortSettings(FILE *f, char *name)
\r
1512 { // wrapper to shield back-end from DCB
\r
1513 PrintCommSettings(f, name, &dcb);
\r
1517 MySearchPath(char *installDir, char *name, char *fullname)
\r
1519 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1520 if(name[0]== '%') {
\r
1521 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1522 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1523 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1524 *strchr(buf, '%') = 0;
\r
1525 strcat(fullname, getenv(buf));
\r
1526 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1528 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1529 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1530 return (int) strlen(fullname);
\r
1532 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1536 MyGetFullPathName(char *name, char *fullname)
\r
1539 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1544 { // [HGM] args: allows testing if main window is realized from back-end
\r
1545 return hwndMain != NULL;
\r
1549 PopUpStartupDialog()
\r
1553 LoadLanguageFile(appData.language);
\r
1554 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1555 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1556 FreeProcInstance(lpProc);
\r
1559 /*---------------------------------------------------------------------------*\
\r
1561 * GDI board drawing routines
\r
1563 \*---------------------------------------------------------------------------*/
\r
1565 /* [AS] Draw square using background texture */
\r
1566 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1571 return; /* Should never happen! */
\r
1574 SetGraphicsMode( dst, GM_ADVANCED );
\r
1581 /* X reflection */
\r
1586 x.eDx = (FLOAT) dw + dx - 1;
\r
1589 SetWorldTransform( dst, &x );
\r
1592 /* Y reflection */
\r
1598 x.eDy = (FLOAT) dh + dy - 1;
\r
1600 SetWorldTransform( dst, &x );
\r
1608 x.eDx = (FLOAT) dx;
\r
1609 x.eDy = (FLOAT) dy;
\r
1612 SetWorldTransform( dst, &x );
\r
1616 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1624 SetWorldTransform( dst, &x );
\r
1626 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1629 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1631 PM_WP = (int) WhitePawn,
\r
1632 PM_WN = (int) WhiteKnight,
\r
1633 PM_WB = (int) WhiteBishop,
\r
1634 PM_WR = (int) WhiteRook,
\r
1635 PM_WQ = (int) WhiteQueen,
\r
1636 PM_WF = (int) WhiteFerz,
\r
1637 PM_WW = (int) WhiteWazir,
\r
1638 PM_WE = (int) WhiteAlfil,
\r
1639 PM_WM = (int) WhiteMan,
\r
1640 PM_WO = (int) WhiteCannon,
\r
1641 PM_WU = (int) WhiteUnicorn,
\r
1642 PM_WH = (int) WhiteNightrider,
\r
1643 PM_WA = (int) WhiteAngel,
\r
1644 PM_WC = (int) WhiteMarshall,
\r
1645 PM_WAB = (int) WhiteCardinal,
\r
1646 PM_WD = (int) WhiteDragon,
\r
1647 PM_WL = (int) WhiteLance,
\r
1648 PM_WS = (int) WhiteCobra,
\r
1649 PM_WV = (int) WhiteFalcon,
\r
1650 PM_WSG = (int) WhiteSilver,
\r
1651 PM_WG = (int) WhiteGrasshopper,
\r
1652 PM_WK = (int) WhiteKing,
\r
1653 PM_BP = (int) BlackPawn,
\r
1654 PM_BN = (int) BlackKnight,
\r
1655 PM_BB = (int) BlackBishop,
\r
1656 PM_BR = (int) BlackRook,
\r
1657 PM_BQ = (int) BlackQueen,
\r
1658 PM_BF = (int) BlackFerz,
\r
1659 PM_BW = (int) BlackWazir,
\r
1660 PM_BE = (int) BlackAlfil,
\r
1661 PM_BM = (int) BlackMan,
\r
1662 PM_BO = (int) BlackCannon,
\r
1663 PM_BU = (int) BlackUnicorn,
\r
1664 PM_BH = (int) BlackNightrider,
\r
1665 PM_BA = (int) BlackAngel,
\r
1666 PM_BC = (int) BlackMarshall,
\r
1667 PM_BG = (int) BlackGrasshopper,
\r
1668 PM_BAB = (int) BlackCardinal,
\r
1669 PM_BD = (int) BlackDragon,
\r
1670 PM_BL = (int) BlackLance,
\r
1671 PM_BS = (int) BlackCobra,
\r
1672 PM_BV = (int) BlackFalcon,
\r
1673 PM_BSG = (int) BlackSilver,
\r
1674 PM_BK = (int) BlackKing
\r
1677 static HFONT hPieceFont = NULL;
\r
1678 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1679 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1680 static int fontBitmapSquareSize = 0;
\r
1681 static char pieceToFontChar[(int) EmptySquare] =
\r
1682 { 'p', 'n', 'b', 'r', 'q',
\r
1683 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1684 'k', 'o', 'm', 'v', 't', 'w',
\r
1685 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1688 extern BOOL SetCharTable( char *table, const char * map );
\r
1689 /* [HGM] moved to backend.c */
\r
1691 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1694 BYTE r1 = GetRValue( color );
\r
1695 BYTE g1 = GetGValue( color );
\r
1696 BYTE b1 = GetBValue( color );
\r
1702 /* Create a uniform background first */
\r
1703 hbrush = CreateSolidBrush( color );
\r
1704 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1705 FillRect( hdc, &rc, hbrush );
\r
1706 DeleteObject( hbrush );
\r
1709 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1710 int steps = squareSize / 2;
\r
1713 for( i=0; i<steps; i++ ) {
\r
1714 BYTE r = r1 - (r1-r2) * i / steps;
\r
1715 BYTE g = g1 - (g1-g2) * i / steps;
\r
1716 BYTE b = b1 - (b1-b2) * i / steps;
\r
1718 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1719 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1720 FillRect( hdc, &rc, hbrush );
\r
1721 DeleteObject(hbrush);
\r
1724 else if( mode == 2 ) {
\r
1725 /* Diagonal gradient, good more or less for every piece */
\r
1726 POINT triangle[3];
\r
1727 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1728 HBRUSH hbrush_old;
\r
1729 int steps = squareSize;
\r
1732 triangle[0].x = squareSize - steps;
\r
1733 triangle[0].y = squareSize;
\r
1734 triangle[1].x = squareSize;
\r
1735 triangle[1].y = squareSize;
\r
1736 triangle[2].x = squareSize;
\r
1737 triangle[2].y = squareSize - steps;
\r
1739 for( i=0; i<steps; i++ ) {
\r
1740 BYTE r = r1 - (r1-r2) * i / steps;
\r
1741 BYTE g = g1 - (g1-g2) * i / steps;
\r
1742 BYTE b = b1 - (b1-b2) * i / steps;
\r
1744 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1745 hbrush_old = SelectObject( hdc, hbrush );
\r
1746 Polygon( hdc, triangle, 3 );
\r
1747 SelectObject( hdc, hbrush_old );
\r
1748 DeleteObject(hbrush);
\r
1753 SelectObject( hdc, hpen );
\r
1758 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1759 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1760 piece: follow the steps as explained below.
\r
1762 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1766 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1770 int backColor = whitePieceColor;
\r
1771 int foreColor = blackPieceColor;
\r
1773 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1774 backColor = appData.fontBackColorWhite;
\r
1775 foreColor = appData.fontForeColorWhite;
\r
1777 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1778 backColor = appData.fontBackColorBlack;
\r
1779 foreColor = appData.fontForeColorBlack;
\r
1783 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1785 hbm_old = SelectObject( hdc, hbm );
\r
1789 rc.right = squareSize;
\r
1790 rc.bottom = squareSize;
\r
1792 /* Step 1: background is now black */
\r
1793 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1795 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1797 pt.x = (squareSize - sz.cx) / 2;
\r
1798 pt.y = (squareSize - sz.cy) / 2;
\r
1800 SetBkMode( hdc, TRANSPARENT );
\r
1801 SetTextColor( hdc, chroma );
\r
1802 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1803 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1805 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1806 /* Step 3: the area outside the piece is filled with white */
\r
1807 // FloodFill( hdc, 0, 0, chroma );
\r
1808 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1809 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1810 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1811 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1812 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1814 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1815 but if the start point is not inside the piece we're lost!
\r
1816 There should be a better way to do this... if we could create a region or path
\r
1817 from the fill operation we would be fine for example.
\r
1819 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1820 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1822 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1823 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1824 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1826 SelectObject( dc2, bm2 );
\r
1827 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1828 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1829 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1830 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1831 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1834 DeleteObject( bm2 );
\r
1837 SetTextColor( hdc, 0 );
\r
1839 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1840 draw the piece again in black for safety.
\r
1842 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1844 SelectObject( hdc, hbm_old );
\r
1846 if( hPieceMask[index] != NULL ) {
\r
1847 DeleteObject( hPieceMask[index] );
\r
1850 hPieceMask[index] = hbm;
\r
1853 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1855 SelectObject( hdc, hbm );
\r
1858 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1859 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1860 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1862 SelectObject( dc1, hPieceMask[index] );
\r
1863 SelectObject( dc2, bm2 );
\r
1864 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1865 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1868 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1869 the piece background and deletes (makes transparent) the rest.
\r
1870 Thanks to that mask, we are free to paint the background with the greates
\r
1871 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1872 We use this, to make gradients and give the pieces a "roundish" look.
\r
1874 SetPieceBackground( hdc, backColor, 2 );
\r
1875 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1879 DeleteObject( bm2 );
\r
1882 SetTextColor( hdc, foreColor );
\r
1883 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1885 SelectObject( hdc, hbm_old );
\r
1887 if( hPieceFace[index] != NULL ) {
\r
1888 DeleteObject( hPieceFace[index] );
\r
1891 hPieceFace[index] = hbm;
\r
1894 static int TranslatePieceToFontPiece( int piece )
\r
1924 case BlackMarshall:
\r
1928 case BlackNightrider:
\r
1934 case BlackUnicorn:
\r
1938 case BlackGrasshopper:
\r
1950 case BlackCardinal:
\r
1957 case WhiteMarshall:
\r
1961 case WhiteNightrider:
\r
1967 case WhiteUnicorn:
\r
1971 case WhiteGrasshopper:
\r
1983 case WhiteCardinal:
\r
1992 void CreatePiecesFromFont()
\r
1995 HDC hdc_window = NULL;
\r
2001 if( fontBitmapSquareSize < 0 ) {
\r
2002 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2006 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2007 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2008 fontBitmapSquareSize = -1;
\r
2012 if( fontBitmapSquareSize != squareSize ) {
\r
2013 hdc_window = GetDC( hwndMain );
\r
2014 hdc = CreateCompatibleDC( hdc_window );
\r
2016 if( hPieceFont != NULL ) {
\r
2017 DeleteObject( hPieceFont );
\r
2020 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2021 hPieceMask[i] = NULL;
\r
2022 hPieceFace[i] = NULL;
\r
2028 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2029 fontHeight = appData.fontPieceSize;
\r
2032 fontHeight = (fontHeight * squareSize) / 100;
\r
2034 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2036 lf.lfEscapement = 0;
\r
2037 lf.lfOrientation = 0;
\r
2038 lf.lfWeight = FW_NORMAL;
\r
2040 lf.lfUnderline = 0;
\r
2041 lf.lfStrikeOut = 0;
\r
2042 lf.lfCharSet = DEFAULT_CHARSET;
\r
2043 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2044 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2045 lf.lfQuality = PROOF_QUALITY;
\r
2046 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2047 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2048 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2050 hPieceFont = CreateFontIndirect( &lf );
\r
2052 if( hPieceFont == NULL ) {
\r
2053 fontBitmapSquareSize = -2;
\r
2056 /* Setup font-to-piece character table */
\r
2057 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2058 /* No (or wrong) global settings, try to detect the font */
\r
2059 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2061 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2063 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2064 /* DiagramTT* family */
\r
2065 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2067 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2068 /* Fairy symbols */
\r
2069 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2071 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2072 /* Good Companion (Some characters get warped as literal :-( */
\r
2073 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2074 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2075 SetCharTable(pieceToFontChar, s);
\r
2078 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2079 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2083 /* Create bitmaps */
\r
2084 hfont_old = SelectObject( hdc, hPieceFont );
\r
2085 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2086 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2087 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2089 SelectObject( hdc, hfont_old );
\r
2091 fontBitmapSquareSize = squareSize;
\r
2095 if( hdc != NULL ) {
\r
2099 if( hdc_window != NULL ) {
\r
2100 ReleaseDC( hwndMain, hdc_window );
\r
2105 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2109 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2110 if (gameInfo.event &&
\r
2111 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2112 strcmp(name, "k80s") == 0) {
\r
2113 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2115 return LoadBitmap(hinst, name);
\r
2119 /* Insert a color into the program's logical palette
\r
2120 structure. This code assumes the given color is
\r
2121 the result of the RGB or PALETTERGB macro, and it
\r
2122 knows how those macros work (which is documented).
\r
2125 InsertInPalette(COLORREF color)
\r
2127 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2129 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2130 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2131 pLogPal->palNumEntries--;
\r
2135 pe->peFlags = (char) 0;
\r
2136 pe->peRed = (char) (0xFF & color);
\r
2137 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2138 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2144 InitDrawingColors()
\r
2146 if (pLogPal == NULL) {
\r
2147 /* Allocate enough memory for a logical palette with
\r
2148 * PALETTESIZE entries and set the size and version fields
\r
2149 * of the logical palette structure.
\r
2151 pLogPal = (NPLOGPALETTE)
\r
2152 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2153 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2154 pLogPal->palVersion = 0x300;
\r
2156 pLogPal->palNumEntries = 0;
\r
2158 InsertInPalette(lightSquareColor);
\r
2159 InsertInPalette(darkSquareColor);
\r
2160 InsertInPalette(whitePieceColor);
\r
2161 InsertInPalette(blackPieceColor);
\r
2162 InsertInPalette(highlightSquareColor);
\r
2163 InsertInPalette(premoveHighlightColor);
\r
2165 /* create a logical color palette according the information
\r
2166 * in the LOGPALETTE structure.
\r
2168 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2170 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2171 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2172 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2173 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2174 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2175 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2176 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2177 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2178 /* [AS] Force rendering of the font-based pieces */
\r
2179 if( fontBitmapSquareSize > 0 ) {
\r
2180 fontBitmapSquareSize = 0;
\r
2186 BoardWidth(int boardSize, int n)
\r
2187 { /* [HGM] argument n added to allow different width and height */
\r
2188 int lineGap = sizeInfo[boardSize].lineGap;
\r
2190 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2191 lineGap = appData.overrideLineGap;
\r
2194 return (n + 1) * lineGap +
\r
2195 n * sizeInfo[boardSize].squareSize;
\r
2198 /* Respond to board resize by dragging edge */
\r
2200 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2202 BoardSize newSize = NUM_SIZES - 1;
\r
2203 static int recurse = 0;
\r
2204 if (IsIconic(hwndMain)) return;
\r
2205 if (recurse > 0) return;
\r
2207 while (newSize > 0) {
\r
2208 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2209 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2210 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2213 boardSize = newSize;
\r
2214 InitDrawingSizes(boardSize, flags);
\r
2219 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2222 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2224 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2225 ChessSquare piece;
\r
2226 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2228 SIZE clockSize, messageSize;
\r
2230 char buf[MSG_SIZ];
\r
2232 HMENU hmenu = GetMenu(hwndMain);
\r
2233 RECT crect, wrect, oldRect;
\r
2235 LOGBRUSH logbrush;
\r
2237 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2238 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2240 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2241 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2243 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2244 oldRect.top = wpMain.y;
\r
2245 oldRect.right = wpMain.x + wpMain.width;
\r
2246 oldRect.bottom = wpMain.y + wpMain.height;
\r
2248 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2249 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2250 squareSize = sizeInfo[boardSize].squareSize;
\r
2251 lineGap = sizeInfo[boardSize].lineGap;
\r
2252 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2254 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2255 lineGap = appData.overrideLineGap;
\r
2258 if (tinyLayout != oldTinyLayout) {
\r
2259 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2261 style &= ~WS_SYSMENU;
\r
2262 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2263 "&Minimize\tCtrl+F4");
\r
2265 style |= WS_SYSMENU;
\r
2266 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2268 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2270 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2271 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2272 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2274 DrawMenuBar(hwndMain);
\r
2277 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2278 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2280 /* Get text area sizes */
\r
2281 hdc = GetDC(hwndMain);
\r
2282 if (appData.clockMode) {
\r
2283 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2285 snprintf(buf, MSG_SIZ, _("White"));
\r
2287 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2288 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2289 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2290 str = _("We only care about the height here");
\r
2291 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2292 SelectObject(hdc, oldFont);
\r
2293 ReleaseDC(hwndMain, hdc);
\r
2295 /* Compute where everything goes */
\r
2296 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2297 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2298 logoHeight = 2*clockSize.cy;
\r
2299 leftLogoRect.left = OUTER_MARGIN;
\r
2300 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2301 leftLogoRect.top = OUTER_MARGIN;
\r
2302 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2304 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2305 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2306 rightLogoRect.top = OUTER_MARGIN;
\r
2307 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2310 whiteRect.left = leftLogoRect.right;
\r
2311 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2312 whiteRect.top = OUTER_MARGIN;
\r
2313 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2315 blackRect.right = rightLogoRect.left;
\r
2316 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2317 blackRect.top = whiteRect.top;
\r
2318 blackRect.bottom = whiteRect.bottom;
\r
2320 whiteRect.left = OUTER_MARGIN;
\r
2321 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2322 whiteRect.top = OUTER_MARGIN;
\r
2323 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2325 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2326 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2327 blackRect.top = whiteRect.top;
\r
2328 blackRect.bottom = whiteRect.bottom;
\r
2330 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2333 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2334 if (appData.showButtonBar) {
\r
2335 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2336 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2338 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2340 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2341 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2343 boardRect.left = OUTER_MARGIN;
\r
2344 boardRect.right = boardRect.left + boardWidth;
\r
2345 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2346 boardRect.bottom = boardRect.top + boardHeight;
\r
2348 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2349 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2350 oldBoardSize = boardSize;
\r
2351 oldTinyLayout = tinyLayout;
\r
2352 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2353 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2354 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2355 winW *= 1 + twoBoards;
\r
2356 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2357 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2358 wpMain.height = winH; // without disturbing window attachments
\r
2359 GetWindowRect(hwndMain, &wrect);
\r
2360 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2361 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2363 // [HGM] placement: let attached windows follow size change.
\r
2364 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2365 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2366 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2367 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2368 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2370 /* compensate if menu bar wrapped */
\r
2371 GetClientRect(hwndMain, &crect);
\r
2372 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2373 wpMain.height += offby;
\r
2375 case WMSZ_TOPLEFT:
\r
2376 SetWindowPos(hwndMain, NULL,
\r
2377 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2378 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2381 case WMSZ_TOPRIGHT:
\r
2383 SetWindowPos(hwndMain, NULL,
\r
2384 wrect.left, wrect.bottom - wpMain.height,
\r
2385 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2388 case WMSZ_BOTTOMLEFT:
\r
2390 SetWindowPos(hwndMain, NULL,
\r
2391 wrect.right - wpMain.width, wrect.top,
\r
2392 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2395 case WMSZ_BOTTOMRIGHT:
\r
2399 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2400 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2405 for (i = 0; i < N_BUTTONS; i++) {
\r
2406 if (buttonDesc[i].hwnd != NULL) {
\r
2407 DestroyWindow(buttonDesc[i].hwnd);
\r
2408 buttonDesc[i].hwnd = NULL;
\r
2410 if (appData.showButtonBar) {
\r
2411 buttonDesc[i].hwnd =
\r
2412 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2413 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2414 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2415 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2416 (HMENU) buttonDesc[i].id,
\r
2417 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2419 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2420 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2421 MAKELPARAM(FALSE, 0));
\r
2423 if (buttonDesc[i].id == IDM_Pause)
\r
2424 hwndPause = buttonDesc[i].hwnd;
\r
2425 buttonDesc[i].wndproc = (WNDPROC)
\r
2426 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2429 if (gridPen != NULL) DeleteObject(gridPen);
\r
2430 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2431 if (premovePen != NULL) DeleteObject(premovePen);
\r
2432 if (lineGap != 0) {
\r
2433 logbrush.lbStyle = BS_SOLID;
\r
2434 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2436 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2437 lineGap, &logbrush, 0, NULL);
\r
2438 logbrush.lbColor = highlightSquareColor;
\r
2440 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2441 lineGap, &logbrush, 0, NULL);
\r
2443 logbrush.lbColor = premoveHighlightColor;
\r
2445 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2446 lineGap, &logbrush, 0, NULL);
\r
2448 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2449 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2450 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2451 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2452 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2453 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2454 BOARD_WIDTH * (squareSize + lineGap);
\r
2455 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2457 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2458 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2459 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2460 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2461 lineGap / 2 + (i * (squareSize + lineGap));
\r
2462 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2463 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2464 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2468 /* [HGM] Licensing requirement */
\r
2470 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2473 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2475 GothicPopUp( "", VariantNormal);
\r
2478 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2480 /* Load piece bitmaps for this board size */
\r
2481 for (i=0; i<=2; i++) {
\r
2482 for (piece = WhitePawn;
\r
2483 (int) piece < (int) BlackPawn;
\r
2484 piece = (ChessSquare) ((int) piece + 1)) {
\r
2485 if (pieceBitmap[i][piece] != NULL)
\r
2486 DeleteObject(pieceBitmap[i][piece]);
\r
2490 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2491 // Orthodox Chess pieces
\r
2492 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2493 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2494 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2495 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2496 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2497 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2498 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2499 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2500 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2501 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2502 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2503 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2504 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2505 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2506 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2507 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2508 // in Shogi, Hijack the unused Queen for Lance
\r
2509 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2510 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2511 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2513 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2518 if(squareSize <= 72 && squareSize >= 33) {
\r
2519 /* A & C are available in most sizes now */
\r
2520 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2521 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2524 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2527 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2530 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2533 } else { // Smirf-like
\r
2534 if(gameInfo.variant == VariantSChess) {
\r
2535 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2536 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2537 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2544 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2545 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2548 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2549 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2550 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2551 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2552 } else { // WinBoard standard
\r
2553 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2560 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2561 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2564 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2576 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2585 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2588 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2592 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2593 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2596 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2599 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2600 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2601 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2602 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2603 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2604 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2606 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2609 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2610 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2611 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2612 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2613 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2614 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2615 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2616 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2617 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2620 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2621 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2622 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2623 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2624 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2625 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2626 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2627 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2628 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2629 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2630 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2631 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2632 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2633 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2634 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2638 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2639 /* special Shogi support in this size */
\r
2640 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2641 for (piece = WhitePawn;
\r
2642 (int) piece < (int) BlackPawn;
\r
2643 piece = (ChessSquare) ((int) piece + 1)) {
\r
2644 if (pieceBitmap[i][piece] != NULL)
\r
2645 DeleteObject(pieceBitmap[i][piece]);
\r
2648 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2649 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2650 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2651 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2652 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2653 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2654 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2655 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2656 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2657 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2658 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2659 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2660 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2661 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2662 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2663 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2664 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2665 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2666 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2667 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2668 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2669 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2670 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2671 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2672 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2673 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2674 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2675 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2676 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2677 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2678 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2679 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2680 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2681 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2682 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2683 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2684 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2685 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2686 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2687 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2688 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2689 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2695 PieceBitmap(ChessSquare p, int kind)
\r
2697 if ((int) p >= (int) BlackPawn)
\r
2698 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2700 return pieceBitmap[kind][(int) p];
\r
2703 /***************************************************************/
\r
2705 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2706 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2708 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2709 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2713 SquareToPos(int row, int column, int * x, int * y)
\r
2716 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2717 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2719 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2720 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2725 DrawCoordsOnDC(HDC hdc)
\r
2727 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2728 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2729 char str[2] = { NULLCHAR, NULLCHAR };
\r
2730 int oldMode, oldAlign, x, y, start, i;
\r
2734 if (!appData.showCoords)
\r
2737 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2739 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2740 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2741 oldAlign = GetTextAlign(hdc);
\r
2742 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2744 y = boardRect.top + lineGap;
\r
2745 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2747 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2748 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2749 str[0] = files[start + i];
\r
2750 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2751 y += squareSize + lineGap;
\r
2754 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2756 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2757 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2758 str[0] = ranks[start + i];
\r
2759 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2760 x += squareSize + lineGap;
\r
2763 SelectObject(hdc, oldBrush);
\r
2764 SetBkMode(hdc, oldMode);
\r
2765 SetTextAlign(hdc, oldAlign);
\r
2766 SelectObject(hdc, oldFont);
\r
2770 DrawGridOnDC(HDC hdc)
\r
2774 if (lineGap != 0) {
\r
2775 oldPen = SelectObject(hdc, gridPen);
\r
2776 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2777 SelectObject(hdc, oldPen);
\r
2781 #define HIGHLIGHT_PEN 0
\r
2782 #define PREMOVE_PEN 1
\r
2785 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2788 HPEN oldPen, hPen;
\r
2789 if (lineGap == 0) return;
\r
2791 x1 = boardRect.left +
\r
2792 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2793 y1 = boardRect.top +
\r
2794 lineGap/2 + y * (squareSize + lineGap);
\r
2796 x1 = boardRect.left +
\r
2797 lineGap/2 + x * (squareSize + lineGap);
\r
2798 y1 = boardRect.top +
\r
2799 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2801 hPen = pen ? premovePen : highlightPen;
\r
2802 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2803 MoveToEx(hdc, x1, y1, NULL);
\r
2804 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2805 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2806 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2807 LineTo(hdc, x1, y1);
\r
2808 SelectObject(hdc, oldPen);
\r
2812 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2815 for (i=0; i<2; i++) {
\r
2816 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2817 DrawHighlightOnDC(hdc, TRUE,
\r
2818 h->sq[i].x, h->sq[i].y,
\r
2823 /* Note: sqcolor is used only in monoMode */
\r
2824 /* Note that this code is largely duplicated in woptions.c,
\r
2825 function DrawSampleSquare, so that needs to be updated too */
\r
2827 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2829 HBITMAP oldBitmap;
\r
2833 if (appData.blindfold) return;
\r
2835 /* [AS] Use font-based pieces if needed */
\r
2836 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2837 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2838 CreatePiecesFromFont();
\r
2840 if( fontBitmapSquareSize == squareSize ) {
\r
2841 int index = TranslatePieceToFontPiece(piece);
\r
2843 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2845 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2846 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2850 squareSize, squareSize,
\r
2855 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2857 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2858 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2862 squareSize, squareSize,
\r
2871 if (appData.monoMode) {
\r
2872 SelectObject(tmphdc, PieceBitmap(piece,
\r
2873 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2874 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2875 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2877 tmpSize = squareSize;
\r
2879 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2880 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2881 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2882 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2883 x += (squareSize - minorSize)>>1;
\r
2884 y += squareSize - minorSize - 2;
\r
2885 tmpSize = minorSize;
\r
2887 if (color || appData.allWhite ) {
\r
2888 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2890 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2891 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2892 if(appData.upsideDown && color==flipView)
\r
2893 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2895 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2896 /* Use black for outline of white pieces */
\r
2897 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2898 if(appData.upsideDown && color==flipView)
\r
2899 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2901 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2903 /* Use square color for details of black pieces */
\r
2904 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2905 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2906 if(appData.upsideDown && !flipView)
\r
2907 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2909 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2911 SelectObject(hdc, oldBrush);
\r
2912 SelectObject(tmphdc, oldBitmap);
\r
2916 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2917 int GetBackTextureMode( int algo )
\r
2919 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2923 case BACK_TEXTURE_MODE_PLAIN:
\r
2924 result = 1; /* Always use identity map */
\r
2926 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2927 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2935 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2936 to handle redraws cleanly (as random numbers would always be different).
\r
2938 VOID RebuildTextureSquareInfo()
\r
2948 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2950 if( liteBackTexture != NULL ) {
\r
2951 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2952 lite_w = bi.bmWidth;
\r
2953 lite_h = bi.bmHeight;
\r
2957 if( darkBackTexture != NULL ) {
\r
2958 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2959 dark_w = bi.bmWidth;
\r
2960 dark_h = bi.bmHeight;
\r
2964 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2965 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2966 if( (col + row) & 1 ) {
\r
2968 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2969 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2970 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2972 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2973 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2974 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2976 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2977 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2982 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2983 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2984 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2986 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2987 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2988 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2990 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2991 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2998 /* [AS] Arrow highlighting support */
\r
3000 static double A_WIDTH = 5; /* Width of arrow body */
\r
3002 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3003 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3005 static double Sqr( double x )
\r
3010 static int Round( double x )
\r
3012 return (int) (x + 0.5);
\r
3015 /* Draw an arrow between two points using current settings */
\r
3016 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3019 double dx, dy, j, k, x, y;
\r
3021 if( d_x == s_x ) {
\r
3022 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3024 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3027 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3028 arrow[1].y = d_y - h;
\r
3030 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3031 arrow[2].y = d_y - h;
\r
3036 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3037 arrow[5].y = d_y - h;
\r
3039 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3040 arrow[4].y = d_y - h;
\r
3042 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3045 else if( d_y == s_y ) {
\r
3046 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3049 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3051 arrow[1].x = d_x - w;
\r
3052 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3054 arrow[2].x = d_x - w;
\r
3055 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3060 arrow[5].x = d_x - w;
\r
3061 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3063 arrow[4].x = d_x - w;
\r
3064 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3067 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3070 /* [AS] Needed a lot of paper for this! :-) */
\r
3071 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3072 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3074 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3076 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3081 arrow[0].x = Round(x - j);
\r
3082 arrow[0].y = Round(y + j*dx);
\r
3084 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3085 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3088 x = (double) d_x - k;
\r
3089 y = (double) d_y - k*dy;
\r
3092 x = (double) d_x + k;
\r
3093 y = (double) d_y + k*dy;
\r
3096 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3098 arrow[6].x = Round(x - j);
\r
3099 arrow[6].y = Round(y + j*dx);
\r
3101 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3102 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3104 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3105 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3110 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3111 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3114 Polygon( hdc, arrow, 7 );
\r
3117 /* [AS] Draw an arrow between two squares */
\r
3118 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3120 int s_x, s_y, d_x, d_y;
\r
3127 if( s_col == d_col && s_row == d_row ) {
\r
3131 /* Get source and destination points */
\r
3132 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3133 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3136 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3138 else if( d_y < s_y ) {
\r
3139 d_y += squareSize / 2 + squareSize / 4;
\r
3142 d_y += squareSize / 2;
\r
3146 d_x += squareSize / 2 - squareSize / 4;
\r
3148 else if( d_x < s_x ) {
\r
3149 d_x += squareSize / 2 + squareSize / 4;
\r
3152 d_x += squareSize / 2;
\r
3155 s_x += squareSize / 2;
\r
3156 s_y += squareSize / 2;
\r
3158 /* Adjust width */
\r
3159 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3162 stLB.lbStyle = BS_SOLID;
\r
3163 stLB.lbColor = appData.highlightArrowColor;
\r
3166 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3167 holdpen = SelectObject( hdc, hpen );
\r
3168 hbrush = CreateBrushIndirect( &stLB );
\r
3169 holdbrush = SelectObject( hdc, hbrush );
\r
3171 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3173 SelectObject( hdc, holdpen );
\r
3174 SelectObject( hdc, holdbrush );
\r
3175 DeleteObject( hpen );
\r
3176 DeleteObject( hbrush );
\r
3179 BOOL HasHighlightInfo()
\r
3181 BOOL result = FALSE;
\r
3183 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3184 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3192 BOOL IsDrawArrowEnabled()
\r
3194 BOOL result = FALSE;
\r
3196 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3203 VOID DrawArrowHighlight( HDC hdc )
\r
3205 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3206 DrawArrowBetweenSquares( hdc,
\r
3207 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3208 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3212 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3214 HRGN result = NULL;
\r
3216 if( HasHighlightInfo() ) {
\r
3217 int x1, y1, x2, y2;
\r
3218 int sx, sy, dx, dy;
\r
3220 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3221 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3223 sx = MIN( x1, x2 );
\r
3224 sy = MIN( y1, y2 );
\r
3225 dx = MAX( x1, x2 ) + squareSize;
\r
3226 dy = MAX( y1, y2 ) + squareSize;
\r
3228 result = CreateRectRgn( sx, sy, dx, dy );
\r
3235 Warning: this function modifies the behavior of several other functions.
\r
3237 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3238 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3239 repaint is scattered all over the place, which is not good for features such as
\r
3240 "arrow highlighting" that require a full repaint of the board.
\r
3242 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3243 user interaction, when speed is not so important) but especially to avoid errors
\r
3244 in the displayed graphics.
\r
3246 In such patched places, I always try refer to this function so there is a single
\r
3247 place to maintain knowledge.
\r
3249 To restore the original behavior, just return FALSE unconditionally.
\r
3251 BOOL IsFullRepaintPreferrable()
\r
3253 BOOL result = FALSE;
\r
3255 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3256 /* Arrow may appear on the board */
\r
3264 This function is called by DrawPosition to know whether a full repaint must
\r
3267 Only DrawPosition may directly call this function, which makes use of
\r
3268 some state information. Other function should call DrawPosition specifying
\r
3269 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3271 BOOL DrawPositionNeedsFullRepaint()
\r
3273 BOOL result = FALSE;
\r
3276 Probably a slightly better policy would be to trigger a full repaint
\r
3277 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3278 but animation is fast enough that it's difficult to notice.
\r
3280 if( animInfo.piece == EmptySquare ) {
\r
3281 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3290 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3292 int row, column, x, y, square_color, piece_color;
\r
3293 ChessSquare piece;
\r
3295 HDC texture_hdc = NULL;
\r
3297 /* [AS] Initialize background textures if needed */
\r
3298 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3299 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3300 if( backTextureSquareSize != squareSize
\r
3301 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3302 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3303 backTextureSquareSize = squareSize;
\r
3304 RebuildTextureSquareInfo();
\r
3307 texture_hdc = CreateCompatibleDC( hdc );
\r
3310 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3311 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3313 SquareToPos(row, column, &x, &y);
\r
3315 piece = board[row][column];
\r
3317 square_color = ((column + row) % 2) == 1;
\r
3318 if( gameInfo.variant == VariantXiangqi ) {
\r
3319 square_color = !InPalace(row, column);
\r
3320 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3321 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3323 piece_color = (int) piece < (int) BlackPawn;
\r
3326 /* [HGM] holdings file: light square or black */
\r
3327 if(column == BOARD_LEFT-2) {
\r
3328 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3331 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3335 if(column == BOARD_RGHT + 1 ) {
\r
3336 if( row < gameInfo.holdingsSize )
\r
3339 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3343 if(column == BOARD_LEFT-1 ) /* left align */
\r
3344 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3345 else if( column == BOARD_RGHT) /* right align */
\r
3346 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3348 if (appData.monoMode) {
\r
3349 if (piece == EmptySquare) {
\r
3350 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3351 square_color ? WHITENESS : BLACKNESS);
\r
3353 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3356 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3357 /* [AS] Draw the square using a texture bitmap */
\r
3358 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3359 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3360 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3363 squareSize, squareSize,
\r
3366 backTextureSquareInfo[r][c].mode,
\r
3367 backTextureSquareInfo[r][c].x,
\r
3368 backTextureSquareInfo[r][c].y );
\r
3370 SelectObject( texture_hdc, hbm );
\r
3372 if (piece != EmptySquare) {
\r
3373 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3377 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3379 oldBrush = SelectObject(hdc, brush );
\r
3380 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3381 SelectObject(hdc, oldBrush);
\r
3382 if (piece != EmptySquare)
\r
3383 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3388 if( texture_hdc != NULL ) {
\r
3389 DeleteDC( texture_hdc );
\r
3393 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3394 void fputDW(FILE *f, int x)
\r
3396 fputc(x & 255, f);
\r
3397 fputc(x>>8 & 255, f);
\r
3398 fputc(x>>16 & 255, f);
\r
3399 fputc(x>>24 & 255, f);
\r
3402 #define MAX_CLIPS 200 /* more than enough */
\r
3405 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3407 // HBITMAP bufferBitmap;
\r
3412 int w = 100, h = 50;
\r
3414 if(logo == NULL) {
\r
3415 if(!logoHeight) return;
\r
3416 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3418 // GetClientRect(hwndMain, &Rect);
\r
3419 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3420 // Rect.bottom-Rect.top+1);
\r
3421 tmphdc = CreateCompatibleDC(hdc);
\r
3422 hbm = SelectObject(tmphdc, logo);
\r
3423 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3427 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3428 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3429 SelectObject(tmphdc, hbm);
\r
3437 HDC hdc = GetDC(hwndMain);
\r
3438 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3439 if(appData.autoLogo) {
\r
3441 switch(gameMode) { // pick logos based on game mode
\r
3442 case IcsObserving:
\r
3443 whiteLogo = second.programLogo; // ICS logo
\r
3444 blackLogo = second.programLogo;
\r
3447 case IcsPlayingWhite:
\r
3448 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3449 blackLogo = second.programLogo; // ICS logo
\r
3451 case IcsPlayingBlack:
\r
3452 whiteLogo = second.programLogo; // ICS logo
\r
3453 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3455 case TwoMachinesPlay:
\r
3456 if(first.twoMachinesColor[0] == 'b') {
\r
3457 whiteLogo = second.programLogo;
\r
3458 blackLogo = first.programLogo;
\r
3461 case MachinePlaysWhite:
\r
3462 blackLogo = userLogo;
\r
3464 case MachinePlaysBlack:
\r
3465 whiteLogo = userLogo;
\r
3466 blackLogo = first.programLogo;
\r
3469 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3470 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3471 ReleaseDC(hwndMain, hdc);
\r
3476 UpdateLogos(int display)
\r
3477 { // called after loading new engine(s), in tourney or from menu
\r
3478 LoadLogo(&first, 0, FALSE);
\r
3479 LoadLogo(&second, 1, appData.icsActive);
\r
3480 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3481 if(display) DisplayLogos();
\r
3484 static HDC hdcSeek;
\r
3486 // [HGM] seekgraph
\r
3487 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3490 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3491 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3492 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3493 SelectObject( hdcSeek, hp );
\r
3496 // front-end wrapper for drawing functions to do rectangles
\r
3497 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3502 if (hdcSeek == NULL) {
\r
3503 hdcSeek = GetDC(hwndMain);
\r
3504 if (!appData.monoMode) {
\r
3505 SelectPalette(hdcSeek, hPal, FALSE);
\r
3506 RealizePalette(hdcSeek);
\r
3509 hp = SelectObject( hdcSeek, gridPen );
\r
3510 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3511 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3512 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3513 SelectObject( hdcSeek, hp );
\r
3516 // front-end wrapper for putting text in graph
\r
3517 void DrawSeekText(char *buf, int x, int y)
\r
3520 SetBkMode( hdcSeek, TRANSPARENT );
\r
3521 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3522 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3525 void DrawSeekDot(int x, int y, int color)
\r
3527 int square = color & 0x80;
\r
3528 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3529 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3532 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3533 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3535 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3536 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3537 SelectObject(hdcSeek, oldBrush);
\r
3541 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3543 static Board lastReq[2], lastDrawn[2];
\r
3544 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3545 static int lastDrawnFlipView = 0;
\r
3546 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3547 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3550 HBITMAP bufferBitmap;
\r
3551 HBITMAP oldBitmap;
\r
3553 HRGN clips[MAX_CLIPS];
\r
3554 ChessSquare dragged_piece = EmptySquare;
\r
3555 int nr = twoBoards*partnerUp;
\r
3557 /* I'm undecided on this - this function figures out whether a full
\r
3558 * repaint is necessary on its own, so there's no real reason to have the
\r
3559 * caller tell it that. I think this can safely be set to FALSE - but
\r
3560 * if we trust the callers not to request full repaints unnessesarily, then
\r
3561 * we could skip some clipping work. In other words, only request a full
\r
3562 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3563 * gamestart and similar) --Hawk
\r
3565 Boolean fullrepaint = repaint;
\r
3567 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3569 if( DrawPositionNeedsFullRepaint() ) {
\r
3570 fullrepaint = TRUE;
\r
3573 if (board == NULL) {
\r
3574 if (!lastReqValid[nr]) {
\r
3577 board = lastReq[nr];
\r
3579 CopyBoard(lastReq[nr], board);
\r
3580 lastReqValid[nr] = 1;
\r
3583 if (doingSizing) {
\r
3587 if (IsIconic(hwndMain)) {
\r
3591 if (hdc == NULL) {
\r
3592 hdc = GetDC(hwndMain);
\r
3593 if (!appData.monoMode) {
\r
3594 SelectPalette(hdc, hPal, FALSE);
\r
3595 RealizePalette(hdc);
\r
3599 releaseDC = FALSE;
\r
3602 /* Create some work-DCs */
\r
3603 hdcmem = CreateCompatibleDC(hdc);
\r
3604 tmphdc = CreateCompatibleDC(hdc);
\r
3606 /* If dragging is in progress, we temporarely remove the piece */
\r
3607 /* [HGM] or temporarily decrease count if stacked */
\r
3608 /* !! Moved to before board compare !! */
\r
3609 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3610 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3611 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3612 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3613 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3615 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3616 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3617 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3619 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3622 /* Figure out which squares need updating by comparing the
\r
3623 * newest board with the last drawn board and checking if
\r
3624 * flipping has changed.
\r
3626 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3627 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3628 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3629 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3630 SquareToPos(row, column, &x, &y);
\r
3631 clips[num_clips++] =
\r
3632 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3636 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3637 for (i=0; i<2; i++) {
\r
3638 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3639 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3640 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3641 lastDrawnHighlight.sq[i].y >= 0) {
\r
3642 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3643 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3644 clips[num_clips++] =
\r
3645 CreateRectRgn(x - lineGap, y - lineGap,
\r
3646 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3648 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3649 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3650 clips[num_clips++] =
\r
3651 CreateRectRgn(x - lineGap, y - lineGap,
\r
3652 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3656 for (i=0; i<2; i++) {
\r
3657 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3658 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3659 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3660 lastDrawnPremove.sq[i].y >= 0) {
\r
3661 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3662 lastDrawnPremove.sq[i].x, &x, &y);
\r
3663 clips[num_clips++] =
\r
3664 CreateRectRgn(x - lineGap, y - lineGap,
\r
3665 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3667 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3668 premoveHighlightInfo.sq[i].y >= 0) {
\r
3669 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3670 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3671 clips[num_clips++] =
\r
3672 CreateRectRgn(x - lineGap, y - lineGap,
\r
3673 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3677 } else { // nr == 1
\r
3678 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3679 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3680 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3681 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3682 for (i=0; i<2; i++) {
\r
3683 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3684 partnerHighlightInfo.sq[i].y >= 0) {
\r
3685 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3686 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3687 clips[num_clips++] =
\r
3688 CreateRectRgn(x - lineGap, y - lineGap,
\r
3689 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3691 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3692 oldPartnerHighlight.sq[i].y >= 0) {
\r
3693 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3694 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3695 clips[num_clips++] =
\r
3696 CreateRectRgn(x - lineGap, y - lineGap,
\r
3697 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3702 fullrepaint = TRUE;
\r
3705 /* Create a buffer bitmap - this is the actual bitmap
\r
3706 * being written to. When all the work is done, we can
\r
3707 * copy it to the real DC (the screen). This avoids
\r
3708 * the problems with flickering.
\r
3710 GetClientRect(hwndMain, &Rect);
\r
3711 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3712 Rect.bottom-Rect.top+1);
\r
3713 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3714 if (!appData.monoMode) {
\r
3715 SelectPalette(hdcmem, hPal, FALSE);
\r
3718 /* Create clips for dragging */
\r
3719 if (!fullrepaint) {
\r
3720 if (dragInfo.from.x >= 0) {
\r
3721 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3722 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3724 if (dragInfo.start.x >= 0) {
\r
3725 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3726 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3728 if (dragInfo.pos.x >= 0) {
\r
3729 x = dragInfo.pos.x - squareSize / 2;
\r
3730 y = dragInfo.pos.y - squareSize / 2;
\r
3731 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3733 if (dragInfo.lastpos.x >= 0) {
\r
3734 x = dragInfo.lastpos.x - squareSize / 2;
\r
3735 y = dragInfo.lastpos.y - squareSize / 2;
\r
3736 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3740 /* Are we animating a move?
\r
3742 * - remove the piece from the board (temporarely)
\r
3743 * - calculate the clipping region
\r
3745 if (!fullrepaint) {
\r
3746 if (animInfo.piece != EmptySquare) {
\r
3747 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3748 x = boardRect.left + animInfo.lastpos.x;
\r
3749 y = boardRect.top + animInfo.lastpos.y;
\r
3750 x2 = boardRect.left + animInfo.pos.x;
\r
3751 y2 = boardRect.top + animInfo.pos.y;
\r
3752 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3753 /* Slight kludge. The real problem is that after AnimateMove is
\r
3754 done, the position on the screen does not match lastDrawn.
\r
3755 This currently causes trouble only on e.p. captures in
\r
3756 atomic, where the piece moves to an empty square and then
\r
3757 explodes. The old and new positions both had an empty square
\r
3758 at the destination, but animation has drawn a piece there and
\r
3759 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3760 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3764 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3765 if (num_clips == 0)
\r
3766 fullrepaint = TRUE;
\r
3768 /* Set clipping on the memory DC */
\r
3769 if (!fullrepaint) {
\r
3770 SelectClipRgn(hdcmem, clips[0]);
\r
3771 for (x = 1; x < num_clips; x++) {
\r
3772 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3773 abort(); // this should never ever happen!
\r
3777 /* Do all the drawing to the memory DC */
\r
3778 if(explodeInfo.radius) { // [HGM] atomic
\r
3780 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3781 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3782 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3783 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3784 x += squareSize/2;
\r
3785 y += squareSize/2;
\r
3786 if(!fullrepaint) {
\r
3787 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3788 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3790 DrawGridOnDC(hdcmem);
\r
3791 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3792 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3793 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3794 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3795 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3796 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3797 SelectObject(hdcmem, oldBrush);
\r
3799 DrawGridOnDC(hdcmem);
\r
3800 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3801 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3802 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3804 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3805 oldPartnerHighlight = partnerHighlightInfo;
\r
3807 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3809 if(nr == 0) // [HGM] dual: markers only on left board
\r
3810 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3811 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3812 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3813 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3814 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3815 SquareToPos(row, column, &x, &y);
\r
3816 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3817 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3818 SelectObject(hdcmem, oldBrush);
\r
3823 if( appData.highlightMoveWithArrow ) {
\r
3824 DrawArrowHighlight(hdcmem);
\r
3827 DrawCoordsOnDC(hdcmem);
\r
3829 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3830 /* to make sure lastDrawn contains what is actually drawn */
\r
3832 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3833 if (dragged_piece != EmptySquare) {
\r
3834 /* [HGM] or restack */
\r
3835 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3836 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3838 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3839 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3840 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3841 x = dragInfo.pos.x - squareSize / 2;
\r
3842 y = dragInfo.pos.y - squareSize / 2;
\r
3843 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3844 ((int) dragInfo.piece < (int) BlackPawn),
\r
3845 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3848 /* Put the animated piece back into place and draw it */
\r
3849 if (animInfo.piece != EmptySquare) {
\r
3850 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3851 x = boardRect.left + animInfo.pos.x;
\r
3852 y = boardRect.top + animInfo.pos.y;
\r
3853 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3854 ((int) animInfo.piece < (int) BlackPawn),
\r
3855 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3858 /* Release the bufferBitmap by selecting in the old bitmap
\r
3859 * and delete the memory DC
\r
3861 SelectObject(hdcmem, oldBitmap);
\r
3864 /* Set clipping on the target DC */
\r
3865 if (!fullrepaint) {
\r
3866 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3868 GetRgnBox(clips[x], &rect);
\r
3869 DeleteObject(clips[x]);
\r
3870 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3871 rect.right + wpMain.width/2, rect.bottom);
\r
3873 SelectClipRgn(hdc, clips[0]);
\r
3874 for (x = 1; x < num_clips; x++) {
\r
3875 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3876 abort(); // this should never ever happen!
\r
3880 /* Copy the new bitmap onto the screen in one go.
\r
3881 * This way we avoid any flickering
\r
3883 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3884 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3885 boardRect.right - boardRect.left,
\r
3886 boardRect.bottom - boardRect.top,
\r
3887 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3888 if(saveDiagFlag) {
\r
3889 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3890 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3892 GetObject(bufferBitmap, sizeof(b), &b);
\r
3893 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3894 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3895 bih.biWidth = b.bmWidth;
\r
3896 bih.biHeight = b.bmHeight;
\r
3898 bih.biBitCount = b.bmBitsPixel;
\r
3899 bih.biCompression = 0;
\r
3900 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3901 bih.biXPelsPerMeter = 0;
\r
3902 bih.biYPelsPerMeter = 0;
\r
3903 bih.biClrUsed = 0;
\r
3904 bih.biClrImportant = 0;
\r
3905 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3906 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3907 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3908 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3910 wb = b.bmWidthBytes;
\r
3912 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3913 int k = ((int*) pData)[i];
\r
3914 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3915 if(j >= 16) break;
\r
3917 if(j >= nrColors) nrColors = j+1;
\r
3919 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3921 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3922 for(w=0; w<(wb>>2); w+=2) {
\r
3923 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3924 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3925 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3926 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3927 pData[p++] = m | j<<4;
\r
3929 while(p&3) pData[p++] = 0;
\r
3932 wb = ((wb+31)>>5)<<2;
\r
3934 // write BITMAPFILEHEADER
\r
3935 fprintf(diagFile, "BM");
\r
3936 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3937 fputDW(diagFile, 0);
\r
3938 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3939 // write BITMAPINFOHEADER
\r
3940 fputDW(diagFile, 40);
\r
3941 fputDW(diagFile, b.bmWidth);
\r
3942 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3943 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3944 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3945 fputDW(diagFile, 0);
\r
3946 fputDW(diagFile, 0);
\r
3947 fputDW(diagFile, 0);
\r
3948 fputDW(diagFile, 0);
\r
3949 fputDW(diagFile, 0);
\r
3950 fputDW(diagFile, 0);
\r
3951 // write color table
\r
3953 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3954 // write bitmap data
\r
3955 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3956 fputc(pData[i], diagFile);
\r
3961 SelectObject(tmphdc, oldBitmap);
\r
3963 /* Massive cleanup */
\r
3964 for (x = 0; x < num_clips; x++)
\r
3965 DeleteObject(clips[x]);
\r
3968 DeleteObject(bufferBitmap);
\r
3971 ReleaseDC(hwndMain, hdc);
\r
3973 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3975 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3977 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3980 /* CopyBoard(lastDrawn, board);*/
\r
3981 lastDrawnHighlight = highlightInfo;
\r
3982 lastDrawnPremove = premoveHighlightInfo;
\r
3983 lastDrawnFlipView = flipView;
\r
3984 lastDrawnValid[nr] = 1;
\r
3987 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3992 saveDiagFlag = 1; diagFile = f;
\r
3993 HDCDrawPosition(NULL, TRUE, NULL);
\r
4001 /*---------------------------------------------------------------------------*\
\r
4002 | CLIENT PAINT PROCEDURE
\r
4003 | This is the main event-handler for the WM_PAINT message.
\r
4005 \*---------------------------------------------------------------------------*/
\r
4007 PaintProc(HWND hwnd)
\r
4013 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4014 if (IsIconic(hwnd)) {
\r
4015 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4017 if (!appData.monoMode) {
\r
4018 SelectPalette(hdc, hPal, FALSE);
\r
4019 RealizePalette(hdc);
\r
4021 HDCDrawPosition(hdc, 1, NULL);
\r
4022 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4023 flipView = !flipView; partnerUp = !partnerUp;
\r
4024 HDCDrawPosition(hdc, 1, NULL);
\r
4025 flipView = !flipView; partnerUp = !partnerUp;
\r
4028 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4029 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4030 ETO_CLIPPED|ETO_OPAQUE,
\r
4031 &messageRect, messageText, strlen(messageText), NULL);
\r
4032 SelectObject(hdc, oldFont);
\r
4033 DisplayBothClocks();
\r
4036 EndPaint(hwnd,&ps);
\r
4044 * If the user selects on a border boundary, return -1; if off the board,
\r
4045 * return -2. Otherwise map the event coordinate to the square.
\r
4046 * The offset boardRect.left or boardRect.top must already have been
\r
4047 * subtracted from x.
\r
4049 int EventToSquare(x, limit)
\r
4057 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4059 x /= (squareSize + lineGap);
\r
4071 DropEnable dropEnables[] = {
\r
4072 { 'P', DP_Pawn, N_("Pawn") },
\r
4073 { 'N', DP_Knight, N_("Knight") },
\r
4074 { 'B', DP_Bishop, N_("Bishop") },
\r
4075 { 'R', DP_Rook, N_("Rook") },
\r
4076 { 'Q', DP_Queen, N_("Queen") },
\r
4080 SetupDropMenu(HMENU hmenu)
\r
4082 int i, count, enable;
\r
4084 extern char white_holding[], black_holding[];
\r
4085 char item[MSG_SIZ];
\r
4087 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4088 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4089 dropEnables[i].piece);
\r
4091 while (p && *p++ == dropEnables[i].piece) count++;
\r
4092 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4093 enable = count > 0 || !appData.testLegality
\r
4094 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4095 && !appData.icsActive);
\r
4096 ModifyMenu(hmenu, dropEnables[i].command,
\r
4097 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4098 dropEnables[i].command, item);
\r
4102 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4104 dragInfo.lastpos.x = boardRect.left + x;
\r
4105 dragInfo.lastpos.y = boardRect.top + y;
\r
4106 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4107 dragInfo.from.x = fromX;
\r
4108 dragInfo.from.y = fromY;
\r
4109 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4110 dragInfo.start = dragInfo.from;
\r
4111 SetCapture(hwndMain);
\r
4114 void DragPieceEnd(int x, int y)
\r
4117 dragInfo.start.x = dragInfo.start.y = -1;
\r
4118 dragInfo.from = dragInfo.start;
\r
4119 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4122 void ChangeDragPiece(ChessSquare piece)
\r
4124 dragInfo.piece = piece;
\r
4127 /* Event handler for mouse messages */
\r
4129 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4133 static int recursive = 0;
\r
4135 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4138 if (message == WM_MBUTTONUP) {
\r
4139 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4140 to the middle button: we simulate pressing the left button too!
\r
4142 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4143 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4149 pt.x = LOWORD(lParam);
\r
4150 pt.y = HIWORD(lParam);
\r
4151 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4152 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4153 if (!flipView && y >= 0) {
\r
4154 y = BOARD_HEIGHT - 1 - y;
\r
4156 if (flipView && x >= 0) {
\r
4157 x = BOARD_WIDTH - 1 - x;
\r
4160 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4162 switch (message) {
\r
4163 case WM_LBUTTONDOWN:
\r
4164 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4165 ClockClick(flipClock); break;
\r
4166 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4167 ClockClick(!flipClock); break;
\r
4169 dragInfo.start.x = dragInfo.start.y = -1;
\r
4170 dragInfo.from = dragInfo.start;
\r
4171 if(fromX == -1 && frozen) { // not sure where this is for
\r
4172 fromX = fromY = -1;
\r
4173 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4176 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4177 DrawPosition(TRUE, NULL);
\r
4180 case WM_LBUTTONUP:
\r
4181 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4182 DrawPosition(TRUE, NULL);
\r
4185 case WM_MOUSEMOVE:
\r
4186 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4187 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4188 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4189 if ((appData.animateDragging || appData.highlightDragging)
\r
4190 && (wParam & MK_LBUTTON)
\r
4191 && dragInfo.from.x >= 0)
\r
4193 BOOL full_repaint = FALSE;
\r
4195 if (appData.animateDragging) {
\r
4196 dragInfo.pos = pt;
\r
4198 if (appData.highlightDragging) {
\r
4199 SetHighlights(fromX, fromY, x, y);
\r
4200 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4201 full_repaint = TRUE;
\r
4205 DrawPosition( full_repaint, NULL);
\r
4207 dragInfo.lastpos = dragInfo.pos;
\r
4211 case WM_MOUSEWHEEL: // [DM]
\r
4212 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4213 /* Mouse Wheel is being rolled forward
\r
4214 * Play moves forward
\r
4216 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4217 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4218 /* Mouse Wheel is being rolled backward
\r
4219 * Play moves backward
\r
4221 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4222 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4226 case WM_MBUTTONUP:
\r
4227 case WM_RBUTTONUP:
\r
4229 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4232 case WM_MBUTTONDOWN:
\r
4233 case WM_RBUTTONDOWN:
\r
4236 fromX = fromY = -1;
\r
4237 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4238 dragInfo.start.x = dragInfo.start.y = -1;
\r
4239 dragInfo.from = dragInfo.start;
\r
4240 dragInfo.lastpos = dragInfo.pos;
\r
4241 if (appData.highlightDragging) {
\r
4242 ClearHighlights();
\r
4245 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4246 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4247 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4248 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4249 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4253 DrawPosition(TRUE, NULL);
\r
4255 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4258 if (message == WM_MBUTTONDOWN) {
\r
4259 buttonCount = 3; /* even if system didn't think so */
\r
4260 if (wParam & MK_SHIFT)
\r
4261 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4263 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4264 } else { /* message == WM_RBUTTONDOWN */
\r
4265 /* Just have one menu, on the right button. Windows users don't
\r
4266 think to try the middle one, and sometimes other software steals
\r
4267 it, or it doesn't really exist. */
\r
4268 if(gameInfo.variant != VariantShogi)
\r
4269 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4271 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4275 SetCapture(hwndMain);
\r
4278 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4279 SetupDropMenu(hmenu);
\r
4280 MenuPopup(hwnd, pt, hmenu, -1);
\r
4290 /* Preprocess messages for buttons in main window */
\r
4292 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4294 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4297 for (i=0; i<N_BUTTONS; i++) {
\r
4298 if (buttonDesc[i].id == id) break;
\r
4300 if (i == N_BUTTONS) return 0;
\r
4301 switch (message) {
\r
4306 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4307 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4314 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4317 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4318 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4319 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4320 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4322 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4324 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4325 TypeInEvent((char)wParam);
\r
4331 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4334 /* Process messages for Promotion dialog box */
\r
4336 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4340 switch (message) {
\r
4341 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4342 /* Center the dialog over the application window */
\r
4343 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4344 Translate(hDlg, DLG_PromotionKing);
\r
4345 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4346 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4347 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4348 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4349 SW_SHOW : SW_HIDE);
\r
4350 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4351 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4352 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4353 PieceToChar(WhiteAngel) != '~') ||
\r
4354 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4355 PieceToChar(BlackAngel) != '~') ) ?
\r
4356 SW_SHOW : SW_HIDE);
\r
4357 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4358 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4359 PieceToChar(WhiteMarshall) != '~') ||
\r
4360 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4361 PieceToChar(BlackMarshall) != '~') ) ?
\r
4362 SW_SHOW : SW_HIDE);
\r
4363 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4364 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4365 gameInfo.variant != VariantShogi ?
\r
4366 SW_SHOW : SW_HIDE);
\r
4367 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4368 gameInfo.variant != VariantShogi ?
\r
4369 SW_SHOW : SW_HIDE);
\r
4370 if(gameInfo.variant == VariantShogi) {
\r
4371 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4372 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4373 SetWindowText(hDlg, "Promote?");
\r
4375 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4376 gameInfo.variant == VariantSuper ?
\r
4377 SW_SHOW : SW_HIDE);
\r
4380 case WM_COMMAND: /* message: received a command */
\r
4381 switch (LOWORD(wParam)) {
\r
4383 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4384 ClearHighlights();
\r
4385 DrawPosition(FALSE, NULL);
\r
4388 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4391 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4394 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4395 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4398 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4399 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4401 case PB_Chancellor:
\r
4402 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4404 case PB_Archbishop:
\r
4405 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4408 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4413 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4414 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4415 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4416 fromX = fromY = -1;
\r
4417 if (!appData.highlightLastMove) {
\r
4418 ClearHighlights();
\r
4419 DrawPosition(FALSE, NULL);
\r
4426 /* Pop up promotion dialog */
\r
4428 PromotionPopup(HWND hwnd)
\r
4432 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4433 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4434 hwnd, (DLGPROC)lpProc);
\r
4435 FreeProcInstance(lpProc);
\r
4441 DrawPosition(TRUE, NULL);
\r
4442 PromotionPopup(hwndMain);
\r
4445 /* Toggle ShowThinking */
\r
4447 ToggleShowThinking()
\r
4449 appData.showThinking = !appData.showThinking;
\r
4450 ShowThinkingEvent();
\r
4454 LoadGameDialog(HWND hwnd, char* title)
\r
4458 char fileTitle[MSG_SIZ];
\r
4459 f = OpenFileDialog(hwnd, "rb", "",
\r
4460 appData.oldSaveStyle ? "gam" : "pgn",
\r
4462 title, &number, fileTitle, NULL);
\r
4464 cmailMsgLoaded = FALSE;
\r
4465 if (number == 0) {
\r
4466 int error = GameListBuild(f);
\r
4468 DisplayError(_("Cannot build game list"), error);
\r
4469 } else if (!ListEmpty(&gameList) &&
\r
4470 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4471 GameListPopUp(f, fileTitle);
\r
4474 GameListDestroy();
\r
4477 LoadGame(f, number, fileTitle, FALSE);
\r
4481 int get_term_width()
\r
4486 HFONT hfont, hold_font;
\r
4491 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4495 // get the text metrics
\r
4496 hdc = GetDC(hText);
\r
4497 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4498 if (consoleCF.dwEffects & CFE_BOLD)
\r
4499 lf.lfWeight = FW_BOLD;
\r
4500 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4501 lf.lfItalic = TRUE;
\r
4502 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4503 lf.lfStrikeOut = TRUE;
\r
4504 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4505 lf.lfUnderline = TRUE;
\r
4506 hfont = CreateFontIndirect(&lf);
\r
4507 hold_font = SelectObject(hdc, hfont);
\r
4508 GetTextMetrics(hdc, &tm);
\r
4509 SelectObject(hdc, hold_font);
\r
4510 DeleteObject(hfont);
\r
4511 ReleaseDC(hText, hdc);
\r
4513 // get the rectangle
\r
4514 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4516 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4519 void UpdateICSWidth(HWND hText)
\r
4521 LONG old_width, new_width;
\r
4523 new_width = get_term_width(hText, FALSE);
\r
4524 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4525 if (new_width != old_width)
\r
4527 ics_update_width(new_width);
\r
4528 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4533 ChangedConsoleFont()
\r
4536 CHARRANGE tmpsel, sel;
\r
4537 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4538 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4539 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4542 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4543 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4544 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4545 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4546 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4547 * size. This was undocumented in the version of MSVC++ that I had
\r
4548 * when I wrote the code, but is apparently documented now.
\r
4550 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4551 cfmt.bCharSet = f->lf.lfCharSet;
\r
4552 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4553 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4554 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4555 /* Why are the following seemingly needed too? */
\r
4556 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4557 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4558 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4560 tmpsel.cpMax = -1; /*999999?*/
\r
4561 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4562 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4563 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4564 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4566 paraf.cbSize = sizeof(paraf);
\r
4567 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4568 paraf.dxStartIndent = 0;
\r
4569 paraf.dxOffset = WRAP_INDENT;
\r
4570 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4571 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4572 UpdateICSWidth(hText);
\r
4575 /*---------------------------------------------------------------------------*\
\r
4577 * Window Proc for main window
\r
4579 \*---------------------------------------------------------------------------*/
\r
4581 /* Process messages for main window, etc. */
\r
4583 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4586 int wmId, wmEvent;
\r
4590 char fileTitle[MSG_SIZ];
\r
4591 char buf[MSG_SIZ];
\r
4592 static SnapData sd;
\r
4593 static int peek=0;
\r
4595 switch (message) {
\r
4597 case WM_PAINT: /* message: repaint portion of window */
\r
4601 case WM_ERASEBKGND:
\r
4602 if (IsIconic(hwnd)) {
\r
4603 /* Cheat; change the message */
\r
4604 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4606 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4610 case WM_LBUTTONDOWN:
\r
4611 case WM_MBUTTONDOWN:
\r
4612 case WM_RBUTTONDOWN:
\r
4613 case WM_LBUTTONUP:
\r
4614 case WM_MBUTTONUP:
\r
4615 case WM_RBUTTONUP:
\r
4616 case WM_MOUSEMOVE:
\r
4617 case WM_MOUSEWHEEL:
\r
4618 MouseEvent(hwnd, message, wParam, lParam);
\r
4622 if((char)wParam == '\b') {
\r
4623 ForwardEvent(); peek = 0;
\r
4626 JAWS_KBUP_NAVIGATION
\r
4631 if((char)wParam == '\b') {
\r
4632 if(!peek) BackwardEvent(), peek = 1;
\r
4635 JAWS_KBDOWN_NAVIGATION
\r
4641 JAWS_ALT_INTERCEPT
\r
4643 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4644 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4645 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4646 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4648 SendMessage(h, message, wParam, lParam);
\r
4649 } else if(lParam != KF_REPEAT) {
\r
4650 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4651 TypeInEvent((char)wParam);
\r
4652 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4653 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4658 case WM_PALETTECHANGED:
\r
4659 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4661 HDC hdc = GetDC(hwndMain);
\r
4662 SelectPalette(hdc, hPal, TRUE);
\r
4663 nnew = RealizePalette(hdc);
\r
4665 paletteChanged = TRUE;
\r
4666 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4668 ReleaseDC(hwnd, hdc);
\r
4672 case WM_QUERYNEWPALETTE:
\r
4673 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4675 HDC hdc = GetDC(hwndMain);
\r
4676 paletteChanged = FALSE;
\r
4677 SelectPalette(hdc, hPal, FALSE);
\r
4678 nnew = RealizePalette(hdc);
\r
4680 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4682 ReleaseDC(hwnd, hdc);
\r
4687 case WM_COMMAND: /* message: command from application menu */
\r
4688 wmId = LOWORD(wParam);
\r
4689 wmEvent = HIWORD(wParam);
\r
4694 SAY("new game enter a move to play against the computer with white");
\r
4697 case IDM_NewGameFRC:
\r
4698 if( NewGameFRC() == 0 ) {
\r
4703 case IDM_NewVariant:
\r
4704 NewVariantPopup(hwnd);
\r
4707 case IDM_LoadGame:
\r
4708 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4711 case IDM_LoadNextGame:
\r
4715 case IDM_LoadPrevGame:
\r
4719 case IDM_ReloadGame:
\r
4723 case IDM_LoadPosition:
\r
4724 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4725 Reset(FALSE, TRUE);
\r
4728 f = OpenFileDialog(hwnd, "rb", "",
\r
4729 appData.oldSaveStyle ? "pos" : "fen",
\r
4731 _("Load Position from File"), &number, fileTitle, NULL);
\r
4733 LoadPosition(f, number, fileTitle);
\r
4737 case IDM_LoadNextPosition:
\r
4738 ReloadPosition(1);
\r
4741 case IDM_LoadPrevPosition:
\r
4742 ReloadPosition(-1);
\r
4745 case IDM_ReloadPosition:
\r
4746 ReloadPosition(0);
\r
4749 case IDM_SaveGame:
\r
4750 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4751 f = OpenFileDialog(hwnd, "a", defName,
\r
4752 appData.oldSaveStyle ? "gam" : "pgn",
\r
4754 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4756 SaveGame(f, 0, "");
\r
4760 case IDM_SavePosition:
\r
4761 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4762 f = OpenFileDialog(hwnd, "a", defName,
\r
4763 appData.oldSaveStyle ? "pos" : "fen",
\r
4765 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4767 SavePosition(f, 0, "");
\r
4771 case IDM_SaveDiagram:
\r
4772 defName = "diagram";
\r
4773 f = OpenFileDialog(hwnd, "wb", defName,
\r
4776 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4782 case IDM_CopyGame:
\r
4783 CopyGameToClipboard();
\r
4786 case IDM_PasteGame:
\r
4787 PasteGameFromClipboard();
\r
4790 case IDM_CopyGameListToClipboard:
\r
4791 CopyGameListToClipboard();
\r
4794 /* [AS] Autodetect FEN or PGN data */
\r
4795 case IDM_PasteAny:
\r
4796 PasteGameOrFENFromClipboard();
\r
4799 /* [AS] Move history */
\r
4800 case IDM_ShowMoveHistory:
\r
4801 if( MoveHistoryIsUp() ) {
\r
4802 MoveHistoryPopDown();
\r
4805 MoveHistoryPopUp();
\r
4809 /* [AS] Eval graph */
\r
4810 case IDM_ShowEvalGraph:
\r
4811 if( EvalGraphIsUp() ) {
\r
4812 EvalGraphPopDown();
\r
4816 SetFocus(hwndMain);
\r
4820 /* [AS] Engine output */
\r
4821 case IDM_ShowEngineOutput:
\r
4822 if( EngineOutputIsUp() ) {
\r
4823 EngineOutputPopDown();
\r
4826 EngineOutputPopUp();
\r
4830 /* [AS] User adjudication */
\r
4831 case IDM_UserAdjudication_White:
\r
4832 UserAdjudicationEvent( +1 );
\r
4835 case IDM_UserAdjudication_Black:
\r
4836 UserAdjudicationEvent( -1 );
\r
4839 case IDM_UserAdjudication_Draw:
\r
4840 UserAdjudicationEvent( 0 );
\r
4843 /* [AS] Game list options dialog */
\r
4844 case IDM_GameListOptions:
\r
4845 GameListOptions();
\r
4852 case IDM_CopyPosition:
\r
4853 CopyFENToClipboard();
\r
4856 case IDM_PastePosition:
\r
4857 PasteFENFromClipboard();
\r
4860 case IDM_MailMove:
\r
4864 case IDM_ReloadCMailMsg:
\r
4865 Reset(TRUE, TRUE);
\r
4866 ReloadCmailMsgEvent(FALSE);
\r
4869 case IDM_Minimize:
\r
4870 ShowWindow(hwnd, SW_MINIMIZE);
\r
4877 case IDM_MachineWhite:
\r
4878 MachineWhiteEvent();
\r
4880 * refresh the tags dialog only if it's visible
\r
4882 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4884 tags = PGNTags(&gameInfo);
\r
4885 TagsPopUp(tags, CmailMsg());
\r
4888 SAY("computer starts playing white");
\r
4891 case IDM_MachineBlack:
\r
4892 MachineBlackEvent();
\r
4894 * refresh the tags dialog only if it's visible
\r
4896 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4898 tags = PGNTags(&gameInfo);
\r
4899 TagsPopUp(tags, CmailMsg());
\r
4902 SAY("computer starts playing black");
\r
4905 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4906 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4909 case IDM_TwoMachines:
\r
4910 TwoMachinesEvent();
\r
4912 * refresh the tags dialog only if it's visible
\r
4914 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4916 tags = PGNTags(&gameInfo);
\r
4917 TagsPopUp(tags, CmailMsg());
\r
4920 SAY("computer starts playing both sides");
\r
4923 case IDM_AnalysisMode:
\r
4924 if (!first.analysisSupport) {
\r
4925 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4926 DisplayError(buf, 0);
\r
4928 SAY("analyzing current position");
\r
4929 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4930 if (appData.icsActive) {
\r
4931 if (gameMode != IcsObserving) {
\r
4932 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4933 DisplayError(buf, 0);
\r
4934 /* secure check */
\r
4935 if (appData.icsEngineAnalyze) {
\r
4936 if (appData.debugMode)
\r
4937 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4938 ExitAnalyzeMode();
\r
4944 /* if enable, user want disable icsEngineAnalyze */
\r
4945 if (appData.icsEngineAnalyze) {
\r
4946 ExitAnalyzeMode();
\r
4950 appData.icsEngineAnalyze = TRUE;
\r
4951 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4954 if (!appData.showThinking) ToggleShowThinking();
\r
4955 AnalyzeModeEvent();
\r
4959 case IDM_AnalyzeFile:
\r
4960 if (!first.analysisSupport) {
\r
4961 char buf[MSG_SIZ];
\r
4962 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4963 DisplayError(buf, 0);
\r
4965 if (!appData.showThinking) ToggleShowThinking();
\r
4966 AnalyzeFileEvent();
\r
4967 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4968 AnalysisPeriodicEvent(1);
\r
4972 case IDM_IcsClient:
\r
4976 case IDM_EditGame:
\r
4977 case IDM_EditGame2:
\r
4982 case IDM_EditPosition:
\r
4983 case IDM_EditPosition2:
\r
4984 EditPositionEvent();
\r
4985 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4988 case IDM_Training:
\r
4992 case IDM_ShowGameList:
\r
4993 ShowGameListProc();
\r
4996 case IDM_EditProgs1:
\r
4997 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5000 case IDM_EditProgs2:
\r
5001 LoadEnginePopUp(hwndMain);
\r
5002 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
5005 case IDM_EditServers:
\r
5006 EditTagsPopUp(icsNames, &icsNames);
\r
5009 case IDM_EditTags:
\r
5014 case IDM_EditBook:
\r
5018 case IDM_EditComment:
\r
5020 if (commentUp && editComment) {
\r
5023 EditCommentEvent();
\r
5043 case IDM_CallFlag:
\r
5063 case IDM_StopObserving:
\r
5064 StopObservingEvent();
\r
5067 case IDM_StopExamining:
\r
5068 StopExaminingEvent();
\r
5072 UploadGameEvent();
\r
5075 case IDM_TypeInMove:
\r
5076 TypeInEvent('\000');
\r
5079 case IDM_TypeInName:
\r
5080 PopUpNameDialog('\000');
\r
5083 case IDM_Backward:
\r
5085 SetFocus(hwndMain);
\r
5092 SetFocus(hwndMain);
\r
5097 SetFocus(hwndMain);
\r
5102 SetFocus(hwndMain);
\r
5105 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5106 case OPT_GameListPrev:
\r
5107 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5111 RevertEvent(FALSE);
\r
5114 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5115 RevertEvent(TRUE);
\r
5118 case IDM_TruncateGame:
\r
5119 TruncateGameEvent();
\r
5126 case IDM_RetractMove:
\r
5127 RetractMoveEvent();
\r
5130 case IDM_FlipView:
\r
5131 flipView = !flipView;
\r
5132 DrawPosition(FALSE, NULL);
\r
5135 case IDM_FlipClock:
\r
5136 flipClock = !flipClock;
\r
5137 DisplayBothClocks();
\r
5141 case IDM_MuteSounds:
\r
5142 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5143 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5144 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5147 case IDM_GeneralOptions:
\r
5148 GeneralOptionsPopup(hwnd);
\r
5149 DrawPosition(TRUE, NULL);
\r
5152 case IDM_BoardOptions:
\r
5153 BoardOptionsPopup(hwnd);
\r
5156 case IDM_EnginePlayOptions:
\r
5157 EnginePlayOptionsPopup(hwnd);
\r
5160 case IDM_Engine1Options:
\r
5161 EngineOptionsPopup(hwnd, &first);
\r
5164 case IDM_Engine2Options:
\r
5166 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5167 EngineOptionsPopup(hwnd, &second);
\r
5170 case IDM_OptionsUCI:
\r
5171 UciOptionsPopup(hwnd);
\r
5175 TourneyPopup(hwnd);
\r
5178 case IDM_IcsOptions:
\r
5179 IcsOptionsPopup(hwnd);
\r
5183 FontsOptionsPopup(hwnd);
\r
5187 SoundOptionsPopup(hwnd);
\r
5190 case IDM_CommPort:
\r
5191 CommPortOptionsPopup(hwnd);
\r
5194 case IDM_LoadOptions:
\r
5195 LoadOptionsPopup(hwnd);
\r
5198 case IDM_SaveOptions:
\r
5199 SaveOptionsPopup(hwnd);
\r
5202 case IDM_TimeControl:
\r
5203 TimeControlOptionsPopup(hwnd);
\r
5206 case IDM_SaveSettings:
\r
5207 SaveSettings(settingsFileName);
\r
5210 case IDM_SaveSettingsOnExit:
\r
5211 saveSettingsOnExit = !saveSettingsOnExit;
\r
5212 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5213 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5214 MF_CHECKED : MF_UNCHECKED));
\r
5225 case IDM_AboutGame:
\r
5230 appData.debugMode = !appData.debugMode;
\r
5231 if (appData.debugMode) {
\r
5232 char dir[MSG_SIZ];
\r
5233 GetCurrentDirectory(MSG_SIZ, dir);
\r
5234 SetCurrentDirectory(installDir);
\r
5235 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5236 SetCurrentDirectory(dir);
\r
5237 setbuf(debugFP, NULL);
\r
5244 case IDM_HELPCONTENTS:
\r
5245 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5246 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5247 MessageBox (GetFocus(),
\r
5248 _("Unable to activate help"),
\r
5249 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5253 case IDM_HELPSEARCH:
\r
5254 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5255 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5256 MessageBox (GetFocus(),
\r
5257 _("Unable to activate help"),
\r
5258 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5262 case IDM_HELPHELP:
\r
5263 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5264 MessageBox (GetFocus(),
\r
5265 _("Unable to activate help"),
\r
5266 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5271 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5273 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5274 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5275 FreeProcInstance(lpProc);
\r
5278 case IDM_DirectCommand1:
\r
5279 AskQuestionEvent(_("Direct Command"),
\r
5280 _("Send to chess program:"), "", "1");
\r
5282 case IDM_DirectCommand2:
\r
5283 AskQuestionEvent(_("Direct Command"),
\r
5284 _("Send to second chess program:"), "", "2");
\r
5287 case EP_WhitePawn:
\r
5288 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5289 fromX = fromY = -1;
\r
5292 case EP_WhiteKnight:
\r
5293 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5294 fromX = fromY = -1;
\r
5297 case EP_WhiteBishop:
\r
5298 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5299 fromX = fromY = -1;
\r
5302 case EP_WhiteRook:
\r
5303 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5304 fromX = fromY = -1;
\r
5307 case EP_WhiteQueen:
\r
5308 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5309 fromX = fromY = -1;
\r
5312 case EP_WhiteFerz:
\r
5313 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5314 fromX = fromY = -1;
\r
5317 case EP_WhiteWazir:
\r
5318 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5319 fromX = fromY = -1;
\r
5322 case EP_WhiteAlfil:
\r
5323 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5324 fromX = fromY = -1;
\r
5327 case EP_WhiteCannon:
\r
5328 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5329 fromX = fromY = -1;
\r
5332 case EP_WhiteCardinal:
\r
5333 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5334 fromX = fromY = -1;
\r
5337 case EP_WhiteMarshall:
\r
5338 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5339 fromX = fromY = -1;
\r
5342 case EP_WhiteKing:
\r
5343 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5344 fromX = fromY = -1;
\r
5347 case EP_BlackPawn:
\r
5348 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5349 fromX = fromY = -1;
\r
5352 case EP_BlackKnight:
\r
5353 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5354 fromX = fromY = -1;
\r
5357 case EP_BlackBishop:
\r
5358 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5362 case EP_BlackRook:
\r
5363 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5364 fromX = fromY = -1;
\r
5367 case EP_BlackQueen:
\r
5368 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5369 fromX = fromY = -1;
\r
5372 case EP_BlackFerz:
\r
5373 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5374 fromX = fromY = -1;
\r
5377 case EP_BlackWazir:
\r
5378 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5379 fromX = fromY = -1;
\r
5382 case EP_BlackAlfil:
\r
5383 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5387 case EP_BlackCannon:
\r
5388 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5392 case EP_BlackCardinal:
\r
5393 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5394 fromX = fromY = -1;
\r
5397 case EP_BlackMarshall:
\r
5398 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5402 case EP_BlackKing:
\r
5403 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5404 fromX = fromY = -1;
\r
5407 case EP_EmptySquare:
\r
5408 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5409 fromX = fromY = -1;
\r
5412 case EP_ClearBoard:
\r
5413 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5414 fromX = fromY = -1;
\r
5418 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5419 fromX = fromY = -1;
\r
5423 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5424 fromX = fromY = -1;
\r
5428 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5429 fromX = fromY = -1;
\r
5433 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5434 fromX = fromY = -1;
\r
5438 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5439 fromX = fromY = -1;
\r
5443 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5444 fromX = fromY = -1;
\r
5448 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5449 fromX = fromY = -1;
\r
5453 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5454 fromX = fromY = -1;
\r
5458 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5459 fromX = fromY = -1;
\r
5463 barbaric = 0; appData.language = "";
\r
5464 TranslateMenus(0);
\r
5465 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5466 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5467 lastChecked = wmId;
\r
5471 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5472 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5474 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5475 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5476 TranslateMenus(0);
\r
5477 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5478 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5479 lastChecked = wmId;
\r
5482 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5488 case CLOCK_TIMER_ID:
\r
5489 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5490 clockTimerEvent = 0;
\r
5491 DecrementClocks(); /* call into back end */
\r
5493 case LOAD_GAME_TIMER_ID:
\r
5494 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5495 loadGameTimerEvent = 0;
\r
5496 AutoPlayGameLoop(); /* call into back end */
\r
5498 case ANALYSIS_TIMER_ID:
\r
5499 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5500 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5501 AnalysisPeriodicEvent(0);
\r
5503 KillTimer(hwnd, analysisTimerEvent);
\r
5504 analysisTimerEvent = 0;
\r
5507 case DELAYED_TIMER_ID:
\r
5508 KillTimer(hwnd, delayedTimerEvent);
\r
5509 delayedTimerEvent = 0;
\r
5510 delayedTimerCallback();
\r
5515 case WM_USER_Input:
\r
5516 InputEvent(hwnd, message, wParam, lParam);
\r
5519 /* [AS] Also move "attached" child windows */
\r
5520 case WM_WINDOWPOSCHANGING:
\r
5522 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5523 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5525 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5526 /* Window is moving */
\r
5529 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5530 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5531 rcMain.right = wpMain.x + wpMain.width;
\r
5532 rcMain.top = wpMain.y;
\r
5533 rcMain.bottom = wpMain.y + wpMain.height;
\r
5535 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5536 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5537 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5538 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5539 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5540 wpMain.x = lpwp->x;
\r
5541 wpMain.y = lpwp->y;
\r
5546 /* [AS] Snapping */
\r
5547 case WM_ENTERSIZEMOVE:
\r
5548 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5549 if (hwnd == hwndMain) {
\r
5550 doingSizing = TRUE;
\r
5553 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5557 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5558 if (hwnd == hwndMain) {
\r
5559 lastSizing = wParam;
\r
5564 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5565 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5567 case WM_EXITSIZEMOVE:
\r
5568 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5569 if (hwnd == hwndMain) {
\r
5571 doingSizing = FALSE;
\r
5572 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5573 GetClientRect(hwnd, &client);
\r
5574 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5576 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5578 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5581 case WM_DESTROY: /* message: window being destroyed */
\r
5582 PostQuitMessage(0);
\r
5586 if (hwnd == hwndMain) {
\r
5591 default: /* Passes it on if unprocessed */
\r
5592 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5597 /*---------------------------------------------------------------------------*\
\r
5599 * Misc utility routines
\r
5601 \*---------------------------------------------------------------------------*/
\r
5604 * Decent random number generator, at least not as bad as Windows
\r
5605 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5607 unsigned int randstate;
\r
5612 randstate = randstate * 1664525 + 1013904223;
\r
5613 return (int) randstate & 0x7fffffff;
\r
5617 mysrandom(unsigned int seed)
\r
5624 * returns TRUE if user selects a different color, FALSE otherwise
\r
5628 ChangeColor(HWND hwnd, COLORREF *which)
\r
5630 static BOOL firstTime = TRUE;
\r
5631 static DWORD customColors[16];
\r
5633 COLORREF newcolor;
\r
5638 /* Make initial colors in use available as custom colors */
\r
5639 /* Should we put the compiled-in defaults here instead? */
\r
5641 customColors[i++] = lightSquareColor & 0xffffff;
\r
5642 customColors[i++] = darkSquareColor & 0xffffff;
\r
5643 customColors[i++] = whitePieceColor & 0xffffff;
\r
5644 customColors[i++] = blackPieceColor & 0xffffff;
\r
5645 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5646 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5648 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5649 customColors[i++] = textAttribs[ccl].color;
\r
5651 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5652 firstTime = FALSE;
\r
5655 cc.lStructSize = sizeof(cc);
\r
5656 cc.hwndOwner = hwnd;
\r
5657 cc.hInstance = NULL;
\r
5658 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5659 cc.lpCustColors = (LPDWORD) customColors;
\r
5660 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5662 if (!ChooseColor(&cc)) return FALSE;
\r
5664 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5665 if (newcolor == *which) return FALSE;
\r
5666 *which = newcolor;
\r
5670 InitDrawingColors();
\r
5671 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5676 MyLoadSound(MySound *ms)
\r
5682 if (ms->data && ms->flag) free(ms->data);
\r
5685 switch (ms->name[0]) {
\r
5691 /* System sound from Control Panel. Don't preload here. */
\r
5695 if (ms->name[1] == NULLCHAR) {
\r
5696 /* "!" alone = silence */
\r
5699 /* Builtin wave resource. Error if not found. */
\r
5700 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5701 if (h == NULL) break;
\r
5702 ms->data = (void *)LoadResource(hInst, h);
\r
5703 ms->flag = 0; // not maloced, so cannot be freed!
\r
5704 if (h == NULL) break;
\r
5709 /* .wav file. Error if not found. */
\r
5710 f = fopen(ms->name, "rb");
\r
5711 if (f == NULL) break;
\r
5712 if (fstat(fileno(f), &st) < 0) break;
\r
5713 ms->data = malloc(st.st_size);
\r
5715 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5721 char buf[MSG_SIZ];
\r
5722 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5723 DisplayError(buf, GetLastError());
\r
5729 MyPlaySound(MySound *ms)
\r
5731 BOOLEAN ok = FALSE;
\r
5733 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5734 switch (ms->name[0]) {
\r
5736 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5741 /* System sound from Control Panel (deprecated feature).
\r
5742 "$" alone or an unset sound name gets default beep (still in use). */
\r
5743 if (ms->name[1]) {
\r
5744 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5746 if (!ok) ok = MessageBeep(MB_OK);
\r
5749 /* Builtin wave resource, or "!" alone for silence */
\r
5750 if (ms->name[1]) {
\r
5751 if (ms->data == NULL) return FALSE;
\r
5752 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5758 /* .wav file. Error if not found. */
\r
5759 if (ms->data == NULL) return FALSE;
\r
5760 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5763 /* Don't print an error: this can happen innocently if the sound driver
\r
5764 is busy; for instance, if another instance of WinBoard is playing
\r
5765 a sound at about the same time. */
\r
5771 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5774 OPENFILENAME *ofn;
\r
5775 static UINT *number; /* gross that this is static */
\r
5777 switch (message) {
\r
5778 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5779 /* Center the dialog over the application window */
\r
5780 ofn = (OPENFILENAME *) lParam;
\r
5781 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5782 number = (UINT *) ofn->lCustData;
\r
5783 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5787 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5788 Translate(hDlg, 1536);
\r
5789 return FALSE; /* Allow for further processing */
\r
5792 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5793 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5795 return FALSE; /* Allow for further processing */
\r
5801 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5803 static UINT *number;
\r
5804 OPENFILENAME *ofname;
\r
5807 case WM_INITDIALOG:
\r
5808 Translate(hdlg, DLG_IndexNumber);
\r
5809 ofname = (OPENFILENAME *)lParam;
\r
5810 number = (UINT *)(ofname->lCustData);
\r
5813 ofnot = (OFNOTIFY *)lParam;
\r
5814 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5815 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5824 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5825 char *nameFilt, char *dlgTitle, UINT *number,
\r
5826 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5828 OPENFILENAME openFileName;
\r
5829 char buf1[MSG_SIZ];
\r
5832 if (fileName == NULL) fileName = buf1;
\r
5833 if (defName == NULL) {
\r
5834 safeStrCpy(fileName, "*.", 3 );
\r
5835 strcat(fileName, defExt);
\r
5837 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5839 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5840 if (number) *number = 0;
\r
5842 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5843 openFileName.hwndOwner = hwnd;
\r
5844 openFileName.hInstance = (HANDLE) hInst;
\r
5845 openFileName.lpstrFilter = nameFilt;
\r
5846 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5847 openFileName.nMaxCustFilter = 0L;
\r
5848 openFileName.nFilterIndex = 1L;
\r
5849 openFileName.lpstrFile = fileName;
\r
5850 openFileName.nMaxFile = MSG_SIZ;
\r
5851 openFileName.lpstrFileTitle = fileTitle;
\r
5852 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5853 openFileName.lpstrInitialDir = NULL;
\r
5854 openFileName.lpstrTitle = dlgTitle;
\r
5855 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5856 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5857 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5858 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5859 openFileName.nFileOffset = 0;
\r
5860 openFileName.nFileExtension = 0;
\r
5861 openFileName.lpstrDefExt = defExt;
\r
5862 openFileName.lCustData = (LONG) number;
\r
5863 openFileName.lpfnHook = oldDialog ?
\r
5864 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5865 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5867 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5868 GetOpenFileName(&openFileName)) {
\r
5869 /* open the file */
\r
5870 f = fopen(openFileName.lpstrFile, write);
\r
5872 MessageBox(hwnd, _("File open failed"), NULL,
\r
5873 MB_OK|MB_ICONEXCLAMATION);
\r
5877 int err = CommDlgExtendedError();
\r
5878 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5887 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5889 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5892 * Get the first pop-up menu in the menu template. This is the
\r
5893 * menu that TrackPopupMenu displays.
\r
5895 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5896 TranslateOneMenu(10, hmenuTrackPopup);
\r
5898 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5901 * TrackPopup uses screen coordinates, so convert the
\r
5902 * coordinates of the mouse click to screen coordinates.
\r
5904 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5906 /* Draw and track the floating pop-up menu. */
\r
5907 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5908 pt.x, pt.y, 0, hwnd, NULL);
\r
5910 /* Destroy the menu.*/
\r
5911 DestroyMenu(hmenu);
\r
5916 int sizeX, sizeY, newSizeX, newSizeY;
\r
5918 } ResizeEditPlusButtonsClosure;
\r
5921 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5923 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5927 if (hChild == cl->hText) return TRUE;
\r
5928 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5929 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5930 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5931 ScreenToClient(cl->hDlg, &pt);
\r
5932 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5933 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5937 /* Resize a dialog that has a (rich) edit field filling most of
\r
5938 the top, with a row of buttons below */
\r
5940 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5943 int newTextHeight, newTextWidth;
\r
5944 ResizeEditPlusButtonsClosure cl;
\r
5946 /*if (IsIconic(hDlg)) return;*/
\r
5947 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5949 cl.hdwp = BeginDeferWindowPos(8);
\r
5951 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5952 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5953 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5954 if (newTextHeight < 0) {
\r
5955 newSizeY += -newTextHeight;
\r
5956 newTextHeight = 0;
\r
5958 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5959 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5965 cl.newSizeX = newSizeX;
\r
5966 cl.newSizeY = newSizeY;
\r
5967 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5969 EndDeferWindowPos(cl.hdwp);
\r
5972 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5974 RECT rChild, rParent;
\r
5975 int wChild, hChild, wParent, hParent;
\r
5976 int wScreen, hScreen, xNew, yNew;
\r
5979 /* Get the Height and Width of the child window */
\r
5980 GetWindowRect (hwndChild, &rChild);
\r
5981 wChild = rChild.right - rChild.left;
\r
5982 hChild = rChild.bottom - rChild.top;
\r
5984 /* Get the Height and Width of the parent window */
\r
5985 GetWindowRect (hwndParent, &rParent);
\r
5986 wParent = rParent.right - rParent.left;
\r
5987 hParent = rParent.bottom - rParent.top;
\r
5989 /* Get the display limits */
\r
5990 hdc = GetDC (hwndChild);
\r
5991 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5992 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5993 ReleaseDC(hwndChild, hdc);
\r
5995 /* Calculate new X position, then adjust for screen */
\r
5996 xNew = rParent.left + ((wParent - wChild) /2);
\r
5999 } else if ((xNew+wChild) > wScreen) {
\r
6000 xNew = wScreen - wChild;
\r
6003 /* Calculate new Y position, then adjust for screen */
\r
6005 yNew = rParent.top + ((hParent - hChild) /2);
\r
6008 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6013 } else if ((yNew+hChild) > hScreen) {
\r
6014 yNew = hScreen - hChild;
\r
6017 /* Set it, and return */
\r
6018 return SetWindowPos (hwndChild, NULL,
\r
6019 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6022 /* Center one window over another */
\r
6023 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6025 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6028 /*---------------------------------------------------------------------------*\
\r
6030 * Startup Dialog functions
\r
6032 \*---------------------------------------------------------------------------*/
\r
6034 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6036 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6038 while (*cd != NULL) {
\r
6039 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6045 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6047 char buf1[MAX_ARG_LEN];
\r
6050 if (str[0] == '@') {
\r
6051 FILE* f = fopen(str + 1, "r");
\r
6053 DisplayFatalError(str + 1, errno, 2);
\r
6056 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6058 buf1[len] = NULLCHAR;
\r
6062 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6065 char buf[MSG_SIZ];
\r
6066 char *end = strchr(str, '\n');
\r
6067 if (end == NULL) return;
\r
6068 memcpy(buf, str, end - str);
\r
6069 buf[end - str] = NULLCHAR;
\r
6070 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6076 SetStartupDialogEnables(HWND hDlg)
\r
6078 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6079 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6080 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6081 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6082 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6083 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6084 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6085 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6086 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6087 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6088 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6089 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6090 IsDlgButtonChecked(hDlg, OPT_View));
\r
6094 QuoteForFilename(char *filename)
\r
6096 int dquote, space;
\r
6097 dquote = strchr(filename, '"') != NULL;
\r
6098 space = strchr(filename, ' ') != NULL;
\r
6099 if (dquote || space) {
\r
6111 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6113 char buf[MSG_SIZ];
\r
6116 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6117 q = QuoteForFilename(nthcp);
\r
6118 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6119 if (*nthdir != NULLCHAR) {
\r
6120 q = QuoteForFilename(nthdir);
\r
6121 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6123 if (*nthcp == NULLCHAR) {
\r
6124 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6125 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6126 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6127 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6132 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6134 char buf[MSG_SIZ];
\r
6138 switch (message) {
\r
6139 case WM_INITDIALOG:
\r
6140 /* Center the dialog */
\r
6141 CenterWindow (hDlg, GetDesktopWindow());
\r
6142 Translate(hDlg, DLG_Startup);
\r
6143 /* Initialize the dialog items */
\r
6144 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6145 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6146 firstChessProgramNames);
\r
6147 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6148 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6149 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6150 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6151 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6152 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6153 if (*appData.icsHelper != NULLCHAR) {
\r
6154 char *q = QuoteForFilename(appData.icsHelper);
\r
6155 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6157 if (*appData.icsHost == NULLCHAR) {
\r
6158 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6159 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6160 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6161 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6162 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6165 if (appData.icsActive) {
\r
6166 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6168 else if (appData.noChessProgram) {
\r
6169 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6172 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6175 SetStartupDialogEnables(hDlg);
\r
6179 switch (LOWORD(wParam)) {
\r
6181 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6182 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6183 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6185 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6186 ParseArgs(StringGet, &p);
\r
6187 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6188 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6190 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6191 ParseArgs(StringGet, &p);
\r
6192 SwapEngines(singleList); // ... and then make it 'second'
\r
6193 appData.noChessProgram = FALSE;
\r
6194 appData.icsActive = FALSE;
\r
6195 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6196 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6197 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6199 ParseArgs(StringGet, &p);
\r
6200 if (appData.zippyPlay) {
\r
6201 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6202 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6204 ParseArgs(StringGet, &p);
\r
6206 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6207 appData.noChessProgram = TRUE;
\r
6208 appData.icsActive = FALSE;
\r
6210 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6211 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6214 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6215 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6217 ParseArgs(StringGet, &p);
\r
6219 EndDialog(hDlg, TRUE);
\r
6226 case IDM_HELPCONTENTS:
\r
6227 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6228 MessageBox (GetFocus(),
\r
6229 _("Unable to activate help"),
\r
6230 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6235 SetStartupDialogEnables(hDlg);
\r
6243 /*---------------------------------------------------------------------------*\
\r
6245 * About box dialog functions
\r
6247 \*---------------------------------------------------------------------------*/
\r
6249 /* Process messages for "About" dialog box */
\r
6251 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6253 switch (message) {
\r
6254 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6255 /* Center the dialog over the application window */
\r
6256 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6257 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6258 Translate(hDlg, ABOUTBOX);
\r
6262 case WM_COMMAND: /* message: received a command */
\r
6263 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6264 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6265 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6273 /*---------------------------------------------------------------------------*\
\r
6275 * Comment Dialog functions
\r
6277 \*---------------------------------------------------------------------------*/
\r
6280 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6282 static HANDLE hwndText = NULL;
\r
6283 int len, newSizeX, newSizeY, flags;
\r
6284 static int sizeX, sizeY;
\r
6289 switch (message) {
\r
6290 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6291 /* Initialize the dialog items */
\r
6292 Translate(hDlg, DLG_EditComment);
\r
6293 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6294 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6295 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6296 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6297 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6298 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6299 SetWindowText(hDlg, commentTitle);
\r
6300 if (editComment) {
\r
6301 SetFocus(hwndText);
\r
6303 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6305 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6306 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6307 MAKELPARAM(FALSE, 0));
\r
6308 /* Size and position the dialog */
\r
6309 if (!commentDialog) {
\r
6310 commentDialog = hDlg;
\r
6311 flags = SWP_NOZORDER;
\r
6312 GetClientRect(hDlg, &rect);
\r
6313 sizeX = rect.right;
\r
6314 sizeY = rect.bottom;
\r
6315 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6316 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6317 WINDOWPLACEMENT wp;
\r
6318 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6319 wp.length = sizeof(WINDOWPLACEMENT);
\r
6321 wp.showCmd = SW_SHOW;
\r
6322 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6323 wp.rcNormalPosition.left = wpComment.x;
\r
6324 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6325 wp.rcNormalPosition.top = wpComment.y;
\r
6326 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6327 SetWindowPlacement(hDlg, &wp);
\r
6329 GetClientRect(hDlg, &rect);
\r
6330 newSizeX = rect.right;
\r
6331 newSizeY = rect.bottom;
\r
6332 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6333 newSizeX, newSizeY);
\r
6338 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6341 case WM_COMMAND: /* message: received a command */
\r
6342 switch (LOWORD(wParam)) {
\r
6344 if (editComment) {
\r
6346 /* Read changed options from the dialog box */
\r
6347 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6348 len = GetWindowTextLength(hwndText);
\r
6349 str = (char *) malloc(len + 1);
\r
6350 GetWindowText(hwndText, str, len + 1);
\r
6359 ReplaceComment(commentIndex, str);
\r
6366 case OPT_CancelComment:
\r
6370 case OPT_ClearComment:
\r
6371 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6374 case OPT_EditComment:
\r
6375 EditCommentEvent();
\r
6383 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6384 if( wParam == OPT_CommentText ) {
\r
6385 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6387 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6388 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6392 pt.x = LOWORD( lpMF->lParam );
\r
6393 pt.y = HIWORD( lpMF->lParam );
\r
6395 if(lpMF->msg == WM_CHAR) {
\r
6397 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6398 index = sel.cpMin;
\r
6400 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6402 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6403 len = GetWindowTextLength(hwndText);
\r
6404 str = (char *) malloc(len + 1);
\r
6405 GetWindowText(hwndText, str, len + 1);
\r
6406 ReplaceComment(commentIndex, str);
\r
6407 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6408 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6411 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6412 lpMF->msg = WM_USER;
\r
6420 newSizeX = LOWORD(lParam);
\r
6421 newSizeY = HIWORD(lParam);
\r
6422 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6427 case WM_GETMINMAXINFO:
\r
6428 /* Prevent resizing window too small */
\r
6429 mmi = (MINMAXINFO *) lParam;
\r
6430 mmi->ptMinTrackSize.x = 100;
\r
6431 mmi->ptMinTrackSize.y = 100;
\r
6438 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6443 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6445 if (str == NULL) str = "";
\r
6446 p = (char *) malloc(2 * strlen(str) + 2);
\r
6449 if (*str == '\n') *q++ = '\r';
\r
6453 if (commentText != NULL) free(commentText);
\r
6455 commentIndex = index;
\r
6456 commentTitle = title;
\r
6458 editComment = edit;
\r
6460 if (commentDialog) {
\r
6461 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6462 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6464 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6465 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6466 hwndMain, (DLGPROC)lpProc);
\r
6467 FreeProcInstance(lpProc);
\r
6473 /*---------------------------------------------------------------------------*\
\r
6475 * Type-in move dialog functions
\r
6477 \*---------------------------------------------------------------------------*/
\r
6480 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6482 char move[MSG_SIZ];
\r
6485 switch (message) {
\r
6486 case WM_INITDIALOG:
\r
6487 move[0] = (char) lParam;
\r
6488 move[1] = NULLCHAR;
\r
6489 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6490 Translate(hDlg, DLG_TypeInMove);
\r
6491 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6492 SetWindowText(hInput, move);
\r
6494 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6498 switch (LOWORD(wParam)) {
\r
6501 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6502 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6503 TypeInDoneEvent(move);
\r
6504 EndDialog(hDlg, TRUE);
\r
6507 EndDialog(hDlg, FALSE);
\r
6518 PopUpMoveDialog(char firstchar)
\r
6522 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6523 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6524 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6525 FreeProcInstance(lpProc);
\r
6528 /*---------------------------------------------------------------------------*\
\r
6530 * Type-in name dialog functions
\r
6532 \*---------------------------------------------------------------------------*/
\r
6535 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6537 char move[MSG_SIZ];
\r
6540 switch (message) {
\r
6541 case WM_INITDIALOG:
\r
6542 move[0] = (char) lParam;
\r
6543 move[1] = NULLCHAR;
\r
6544 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6545 Translate(hDlg, DLG_TypeInName);
\r
6546 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6547 SetWindowText(hInput, move);
\r
6549 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6553 switch (LOWORD(wParam)) {
\r
6555 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6556 appData.userName = strdup(move);
\r
6559 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6560 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6561 DisplayTitle(move);
\r
6565 EndDialog(hDlg, TRUE);
\r
6568 EndDialog(hDlg, FALSE);
\r
6579 PopUpNameDialog(char firstchar)
\r
6583 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6584 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6585 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6586 FreeProcInstance(lpProc);
\r
6589 /*---------------------------------------------------------------------------*\
\r
6593 \*---------------------------------------------------------------------------*/
\r
6595 /* Nonmodal error box */
\r
6596 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6597 WPARAM wParam, LPARAM lParam);
\r
6600 ErrorPopUp(char *title, char *content)
\r
6604 BOOLEAN modal = hwndMain == NULL;
\r
6622 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6623 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6626 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6628 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6629 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6630 hwndMain, (DLGPROC)lpProc);
\r
6631 FreeProcInstance(lpProc);
\r
6638 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6639 if (errorDialog == NULL) return;
\r
6640 DestroyWindow(errorDialog);
\r
6641 errorDialog = NULL;
\r
6642 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6646 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6651 switch (message) {
\r
6652 case WM_INITDIALOG:
\r
6653 GetWindowRect(hDlg, &rChild);
\r
6656 SetWindowPos(hDlg, NULL, rChild.left,
\r
6657 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6658 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6662 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6663 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6664 and it doesn't work when you resize the dialog.
\r
6665 For now, just give it a default position.
\r
6667 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6668 Translate(hDlg, DLG_Error);
\r
6670 errorDialog = hDlg;
\r
6671 SetWindowText(hDlg, errorTitle);
\r
6672 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6673 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6677 switch (LOWORD(wParam)) {
\r
6680 if (errorDialog == hDlg) errorDialog = NULL;
\r
6681 DestroyWindow(hDlg);
\r
6693 HWND gothicDialog = NULL;
\r
6696 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6700 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6702 switch (message) {
\r
6703 case WM_INITDIALOG:
\r
6704 GetWindowRect(hDlg, &rChild);
\r
6706 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6710 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6711 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6712 and it doesn't work when you resize the dialog.
\r
6713 For now, just give it a default position.
\r
6715 gothicDialog = hDlg;
\r
6716 SetWindowText(hDlg, errorTitle);
\r
6717 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6718 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6722 switch (LOWORD(wParam)) {
\r
6725 if (errorDialog == hDlg) errorDialog = NULL;
\r
6726 DestroyWindow(hDlg);
\r
6738 GothicPopUp(char *title, VariantClass variant)
\r
6741 static char *lastTitle;
\r
6743 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6744 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6746 if(lastTitle != title && gothicDialog != NULL) {
\r
6747 DestroyWindow(gothicDialog);
\r
6748 gothicDialog = NULL;
\r
6750 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6751 title = lastTitle;
\r
6752 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6753 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6754 hwndMain, (DLGPROC)lpProc);
\r
6755 FreeProcInstance(lpProc);
\r
6760 /*---------------------------------------------------------------------------*\
\r
6762 * Ics Interaction console functions
\r
6764 \*---------------------------------------------------------------------------*/
\r
6766 #define HISTORY_SIZE 64
\r
6767 static char *history[HISTORY_SIZE];
\r
6768 int histIn = 0, histP = 0;
\r
6771 SaveInHistory(char *cmd)
\r
6773 if (history[histIn] != NULL) {
\r
6774 free(history[histIn]);
\r
6775 history[histIn] = NULL;
\r
6777 if (*cmd == NULLCHAR) return;
\r
6778 history[histIn] = StrSave(cmd);
\r
6779 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6780 if (history[histIn] != NULL) {
\r
6781 free(history[histIn]);
\r
6782 history[histIn] = NULL;
\r
6788 PrevInHistory(char *cmd)
\r
6791 if (histP == histIn) {
\r
6792 if (history[histIn] != NULL) free(history[histIn]);
\r
6793 history[histIn] = StrSave(cmd);
\r
6795 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6796 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6798 return history[histP];
\r
6804 if (histP == histIn) return NULL;
\r
6805 histP = (histP + 1) % HISTORY_SIZE;
\r
6806 return history[histP];
\r
6810 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6814 hmenu = LoadMenu(hInst, "TextMenu");
\r
6815 h = GetSubMenu(hmenu, 0);
\r
6817 if (strcmp(e->item, "-") == 0) {
\r
6818 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6819 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6820 int flags = MF_STRING, j = 0;
\r
6821 if (e->item[0] == '|') {
\r
6822 flags |= MF_MENUBARBREAK;
\r
6825 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6826 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6834 WNDPROC consoleTextWindowProc;
\r
6837 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6839 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6840 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6844 SetWindowText(hInput, command);
\r
6846 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6848 sel.cpMin = 999999;
\r
6849 sel.cpMax = 999999;
\r
6850 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6855 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6856 if (sel.cpMin == sel.cpMax) {
\r
6857 /* Expand to surrounding word */
\r
6860 tr.chrg.cpMax = sel.cpMin;
\r
6861 tr.chrg.cpMin = --sel.cpMin;
\r
6862 if (sel.cpMin < 0) break;
\r
6863 tr.lpstrText = name;
\r
6864 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6865 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6869 tr.chrg.cpMin = sel.cpMax;
\r
6870 tr.chrg.cpMax = ++sel.cpMax;
\r
6871 tr.lpstrText = name;
\r
6872 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6873 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6876 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6877 MessageBeep(MB_ICONEXCLAMATION);
\r
6881 tr.lpstrText = name;
\r
6882 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6884 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6885 MessageBeep(MB_ICONEXCLAMATION);
\r
6888 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6891 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6892 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6893 SetWindowText(hInput, buf);
\r
6894 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6896 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6897 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6898 SetWindowText(hInput, buf);
\r
6899 sel.cpMin = 999999;
\r
6900 sel.cpMax = 999999;
\r
6901 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6907 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6912 switch (message) {
\r
6914 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6915 if(wParam=='R') return 0;
\r
6918 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6921 sel.cpMin = 999999;
\r
6922 sel.cpMax = 999999;
\r
6923 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6924 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6929 if(wParam != '\022') {
\r
6930 if (wParam == '\t') {
\r
6931 if (GetKeyState(VK_SHIFT) < 0) {
\r
6933 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6934 if (buttonDesc[0].hwnd) {
\r
6935 SetFocus(buttonDesc[0].hwnd);
\r
6937 SetFocus(hwndMain);
\r
6941 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6944 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6945 JAWS_DELETE( SetFocus(hInput); )
\r
6946 SendMessage(hInput, message, wParam, lParam);
\r
6949 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6951 case WM_RBUTTONDOWN:
\r
6952 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6953 /* Move selection here if it was empty */
\r
6955 pt.x = LOWORD(lParam);
\r
6956 pt.y = HIWORD(lParam);
\r
6957 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6958 if (sel.cpMin == sel.cpMax) {
\r
6959 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6960 sel.cpMax = sel.cpMin;
\r
6961 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6963 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6964 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6966 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6967 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6968 if (sel.cpMin == sel.cpMax) {
\r
6969 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6970 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6972 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6973 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6975 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6976 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6977 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6978 MenuPopup(hwnd, pt, hmenu, -1);
\r
6982 case WM_RBUTTONUP:
\r
6983 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6984 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6985 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6989 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6991 return SendMessage(hInput, message, wParam, lParam);
\r
6992 case WM_MBUTTONDOWN:
\r
6993 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6995 switch (LOWORD(wParam)) {
\r
6996 case IDM_QuickPaste:
\r
6998 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6999 if (sel.cpMin == sel.cpMax) {
\r
7000 MessageBeep(MB_ICONEXCLAMATION);
\r
7003 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7004 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7005 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7010 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7013 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7016 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7020 int i = LOWORD(wParam) - IDM_CommandX;
\r
7021 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7022 icsTextMenuEntry[i].command != NULL) {
\r
7023 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7024 icsTextMenuEntry[i].getname,
\r
7025 icsTextMenuEntry[i].immediate);
\r
7033 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7036 WNDPROC consoleInputWindowProc;
\r
7039 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7041 char buf[MSG_SIZ];
\r
7043 static BOOL sendNextChar = FALSE;
\r
7044 static BOOL quoteNextChar = FALSE;
\r
7045 InputSource *is = consoleInputSource;
\r
7049 switch (message) {
\r
7051 if (!appData.localLineEditing || sendNextChar) {
\r
7052 is->buf[0] = (CHAR) wParam;
\r
7054 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7055 sendNextChar = FALSE;
\r
7058 if (quoteNextChar) {
\r
7059 buf[0] = (char) wParam;
\r
7060 buf[1] = NULLCHAR;
\r
7061 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7062 quoteNextChar = FALSE;
\r
7066 case '\r': /* Enter key */
\r
7067 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7068 if (consoleEcho) SaveInHistory(is->buf);
\r
7069 is->buf[is->count++] = '\n';
\r
7070 is->buf[is->count] = NULLCHAR;
\r
7071 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7072 if (consoleEcho) {
\r
7073 ConsoleOutput(is->buf, is->count, TRUE);
\r
7074 } else if (appData.localLineEditing) {
\r
7075 ConsoleOutput("\n", 1, TRUE);
\r
7078 case '\033': /* Escape key */
\r
7079 SetWindowText(hwnd, "");
\r
7080 cf.cbSize = sizeof(CHARFORMAT);
\r
7081 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7082 if (consoleEcho) {
\r
7083 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7085 cf.crTextColor = COLOR_ECHOOFF;
\r
7087 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7088 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7090 case '\t': /* Tab key */
\r
7091 if (GetKeyState(VK_SHIFT) < 0) {
\r
7093 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7096 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7097 if (buttonDesc[0].hwnd) {
\r
7098 SetFocus(buttonDesc[0].hwnd);
\r
7100 SetFocus(hwndMain);
\r
7104 case '\023': /* Ctrl+S */
\r
7105 sendNextChar = TRUE;
\r
7107 case '\021': /* Ctrl+Q */
\r
7108 quoteNextChar = TRUE;
\r
7118 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7119 p = PrevInHistory(buf);
\r
7121 SetWindowText(hwnd, p);
\r
7122 sel.cpMin = 999999;
\r
7123 sel.cpMax = 999999;
\r
7124 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7129 p = NextInHistory();
\r
7131 SetWindowText(hwnd, p);
\r
7132 sel.cpMin = 999999;
\r
7133 sel.cpMax = 999999;
\r
7134 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7140 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7144 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7148 case WM_MBUTTONDOWN:
\r
7149 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7150 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7152 case WM_RBUTTONUP:
\r
7153 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7154 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7155 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7159 hmenu = LoadMenu(hInst, "InputMenu");
\r
7160 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7161 if (sel.cpMin == sel.cpMax) {
\r
7162 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7163 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7165 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7166 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7168 pt.x = LOWORD(lParam);
\r
7169 pt.y = HIWORD(lParam);
\r
7170 MenuPopup(hwnd, pt, hmenu, -1);
\r
7174 switch (LOWORD(wParam)) {
\r
7176 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7178 case IDM_SelectAll:
\r
7180 sel.cpMax = -1; /*999999?*/
\r
7181 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7184 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7187 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7190 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7195 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7198 #define CO_MAX 100000
\r
7199 #define CO_TRIM 1000
\r
7202 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7204 static SnapData sd;
\r
7205 HWND hText, hInput;
\r
7207 static int sizeX, sizeY;
\r
7208 int newSizeX, newSizeY;
\r
7212 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7213 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7215 switch (message) {
\r
7217 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7219 ENLINK *pLink = (ENLINK*)lParam;
\r
7220 if (pLink->msg == WM_LBUTTONUP)
\r
7224 tr.chrg = pLink->chrg;
\r
7225 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7226 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7227 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7228 free(tr.lpstrText);
\r
7232 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7233 hwndConsole = hDlg;
\r
7235 consoleTextWindowProc = (WNDPROC)
\r
7236 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7237 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7238 consoleInputWindowProc = (WNDPROC)
\r
7239 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7240 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7241 Colorize(ColorNormal, TRUE);
\r
7242 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7243 ChangedConsoleFont();
\r
7244 GetClientRect(hDlg, &rect);
\r
7245 sizeX = rect.right;
\r
7246 sizeY = rect.bottom;
\r
7247 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7248 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7249 WINDOWPLACEMENT wp;
\r
7250 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7251 wp.length = sizeof(WINDOWPLACEMENT);
\r
7253 wp.showCmd = SW_SHOW;
\r
7254 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7255 wp.rcNormalPosition.left = wpConsole.x;
\r
7256 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7257 wp.rcNormalPosition.top = wpConsole.y;
\r
7258 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7259 SetWindowPlacement(hDlg, &wp);
\r
7262 // [HGM] Chessknight's change 2004-07-13
\r
7263 else { /* Determine Defaults */
\r
7264 WINDOWPLACEMENT wp;
\r
7265 wpConsole.x = wpMain.width + 1;
\r
7266 wpConsole.y = wpMain.y;
\r
7267 wpConsole.width = screenWidth - wpMain.width;
\r
7268 wpConsole.height = wpMain.height;
\r
7269 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7270 wp.length = sizeof(WINDOWPLACEMENT);
\r
7272 wp.showCmd = SW_SHOW;
\r
7273 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7274 wp.rcNormalPosition.left = wpConsole.x;
\r
7275 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7276 wp.rcNormalPosition.top = wpConsole.y;
\r
7277 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7278 SetWindowPlacement(hDlg, &wp);
\r
7281 // Allow hText to highlight URLs and send notifications on them
\r
7282 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7283 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7284 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7285 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7299 if (IsIconic(hDlg)) break;
\r
7300 newSizeX = LOWORD(lParam);
\r
7301 newSizeY = HIWORD(lParam);
\r
7302 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7303 RECT rectText, rectInput;
\r
7305 int newTextHeight, newTextWidth;
\r
7306 GetWindowRect(hText, &rectText);
\r
7307 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7308 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7309 if (newTextHeight < 0) {
\r
7310 newSizeY += -newTextHeight;
\r
7311 newTextHeight = 0;
\r
7313 SetWindowPos(hText, NULL, 0, 0,
\r
7314 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7315 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7316 pt.x = rectInput.left;
\r
7317 pt.y = rectInput.top + newSizeY - sizeY;
\r
7318 ScreenToClient(hDlg, &pt);
\r
7319 SetWindowPos(hInput, NULL,
\r
7320 pt.x, pt.y, /* needs client coords */
\r
7321 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7322 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7328 case WM_GETMINMAXINFO:
\r
7329 /* Prevent resizing window too small */
\r
7330 mmi = (MINMAXINFO *) lParam;
\r
7331 mmi->ptMinTrackSize.x = 100;
\r
7332 mmi->ptMinTrackSize.y = 100;
\r
7335 /* [AS] Snapping */
\r
7336 case WM_ENTERSIZEMOVE:
\r
7337 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7340 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7343 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7345 case WM_EXITSIZEMOVE:
\r
7346 UpdateICSWidth(hText);
\r
7347 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7350 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7358 if (hwndConsole) return;
\r
7359 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7360 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7365 ConsoleOutput(char* data, int length, int forceVisible)
\r
7370 char buf[CO_MAX+1];
\r
7373 static int delayLF = 0;
\r
7374 CHARRANGE savesel, sel;
\r
7376 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7384 while (length--) {
\r
7392 } else if (*p == '\007') {
\r
7393 MyPlaySound(&sounds[(int)SoundBell]);
\r
7400 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7401 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7402 /* Save current selection */
\r
7403 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7404 exlen = GetWindowTextLength(hText);
\r
7405 /* Find out whether current end of text is visible */
\r
7406 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7407 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7408 /* Trim existing text if it's too long */
\r
7409 if (exlen + (q - buf) > CO_MAX) {
\r
7410 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7413 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7414 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7416 savesel.cpMin -= trim;
\r
7417 savesel.cpMax -= trim;
\r
7418 if (exlen < 0) exlen = 0;
\r
7419 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7420 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7422 /* Append the new text */
\r
7423 sel.cpMin = exlen;
\r
7424 sel.cpMax = exlen;
\r
7425 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7426 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7427 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7428 if (forceVisible || exlen == 0 ||
\r
7429 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7430 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7431 /* Scroll to make new end of text visible if old end of text
\r
7432 was visible or new text is an echo of user typein */
\r
7433 sel.cpMin = 9999999;
\r
7434 sel.cpMax = 9999999;
\r
7435 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7436 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7437 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7438 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7440 if (savesel.cpMax == exlen || forceVisible) {
\r
7441 /* Move insert point to new end of text if it was at the old
\r
7442 end of text or if the new text is an echo of user typein */
\r
7443 sel.cpMin = 9999999;
\r
7444 sel.cpMax = 9999999;
\r
7445 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7447 /* Restore previous selection */
\r
7448 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7450 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7457 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7461 COLORREF oldFg, oldBg;
\r
7465 if(copyNumber > 1)
\r
7466 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7468 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7469 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7470 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7473 rect.right = x + squareSize;
\r
7475 rect.bottom = y + squareSize;
\r
7478 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7479 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7480 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7481 &rect, str, strlen(str), NULL);
\r
7483 (void) SetTextColor(hdc, oldFg);
\r
7484 (void) SetBkColor(hdc, oldBg);
\r
7485 (void) SelectObject(hdc, oldFont);
\r
7489 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7490 RECT *rect, char *color, char *flagFell)
\r
7494 COLORREF oldFg, oldBg;
\r
7497 if (appData.clockMode) {
\r
7499 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7501 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7508 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7509 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7511 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7512 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7514 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7518 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7519 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7520 rect, str, strlen(str), NULL);
\r
7521 if(logoHeight > 0 && appData.clockMode) {
\r
7523 str += strlen(color)+2;
\r
7524 r.top = rect->top + logoHeight/2;
\r
7525 r.left = rect->left;
\r
7526 r.right = rect->right;
\r
7527 r.bottom = rect->bottom;
\r
7528 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7529 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7530 &r, str, strlen(str), NULL);
\r
7532 (void) SetTextColor(hdc, oldFg);
\r
7533 (void) SetBkColor(hdc, oldBg);
\r
7534 (void) SelectObject(hdc, oldFont);
\r
7539 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7545 if( count <= 0 ) {
\r
7546 if (appData.debugMode) {
\r
7547 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7550 return ERROR_INVALID_USER_BUFFER;
\r
7553 ResetEvent(ovl->hEvent);
\r
7554 ovl->Offset = ovl->OffsetHigh = 0;
\r
7555 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7559 err = GetLastError();
\r
7560 if (err == ERROR_IO_PENDING) {
\r
7561 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7565 err = GetLastError();
\r
7572 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7577 ResetEvent(ovl->hEvent);
\r
7578 ovl->Offset = ovl->OffsetHigh = 0;
\r
7579 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7583 err = GetLastError();
\r
7584 if (err == ERROR_IO_PENDING) {
\r
7585 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7589 err = GetLastError();
\r
7595 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7596 void CheckForInputBufferFull( InputSource * is )
\r
7598 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7599 /* Look for end of line */
\r
7600 char * p = is->buf;
\r
7602 while( p < is->next && *p != '\n' ) {
\r
7606 if( p >= is->next ) {
\r
7607 if (appData.debugMode) {
\r
7608 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7611 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7612 is->count = (DWORD) -1;
\r
7613 is->next = is->buf;
\r
7619 InputThread(LPVOID arg)
\r
7624 is = (InputSource *) arg;
\r
7625 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7626 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7627 while (is->hThread != NULL) {
\r
7628 is->error = DoReadFile(is->hFile, is->next,
\r
7629 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7630 &is->count, &ovl);
\r
7631 if (is->error == NO_ERROR) {
\r
7632 is->next += is->count;
\r
7634 if (is->error == ERROR_BROKEN_PIPE) {
\r
7635 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7638 is->count = (DWORD) -1;
\r
7639 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7644 CheckForInputBufferFull( is );
\r
7646 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7648 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7650 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7653 CloseHandle(ovl.hEvent);
\r
7654 CloseHandle(is->hFile);
\r
7656 if (appData.debugMode) {
\r
7657 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7664 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7666 NonOvlInputThread(LPVOID arg)
\r
7673 is = (InputSource *) arg;
\r
7674 while (is->hThread != NULL) {
\r
7675 is->error = ReadFile(is->hFile, is->next,
\r
7676 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7677 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7678 if (is->error == NO_ERROR) {
\r
7679 /* Change CRLF to LF */
\r
7680 if (is->next > is->buf) {
\r
7682 i = is->count + 1;
\r
7690 if (prev == '\r' && *p == '\n') {
\r
7702 if (is->error == ERROR_BROKEN_PIPE) {
\r
7703 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7706 is->count = (DWORD) -1;
\r
7710 CheckForInputBufferFull( is );
\r
7712 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7714 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7716 if (is->count < 0) break; /* Quit on error */
\r
7718 CloseHandle(is->hFile);
\r
7723 SocketInputThread(LPVOID arg)
\r
7727 is = (InputSource *) arg;
\r
7728 while (is->hThread != NULL) {
\r
7729 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7730 if ((int)is->count == SOCKET_ERROR) {
\r
7731 is->count = (DWORD) -1;
\r
7732 is->error = WSAGetLastError();
\r
7734 is->error = NO_ERROR;
\r
7735 is->next += is->count;
\r
7736 if (is->count == 0 && is->second == is) {
\r
7737 /* End of file on stderr; quit with no message */
\r
7741 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7743 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7745 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7751 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7755 is = (InputSource *) lParam;
\r
7756 if (is->lineByLine) {
\r
7757 /* Feed in lines one by one */
\r
7758 char *p = is->buf;
\r
7760 while (q < is->next) {
\r
7761 if (*q++ == '\n') {
\r
7762 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7767 /* Move any partial line to the start of the buffer */
\r
7769 while (p < is->next) {
\r
7774 if (is->error != NO_ERROR || is->count == 0) {
\r
7775 /* Notify backend of the error. Note: If there was a partial
\r
7776 line at the end, it is not flushed through. */
\r
7777 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7780 /* Feed in the whole chunk of input at once */
\r
7781 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7782 is->next = is->buf;
\r
7786 /*---------------------------------------------------------------------------*\
\r
7788 * Menu enables. Used when setting various modes.
\r
7790 \*---------------------------------------------------------------------------*/
\r
7798 GreyRevert(Boolean grey)
\r
7799 { // [HGM] vari: for retracting variations in local mode
\r
7800 HMENU hmenu = GetMenu(hwndMain);
\r
7801 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7802 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7806 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7808 while (enab->item > 0) {
\r
7809 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7814 Enables gnuEnables[] = {
\r
7815 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7829 // Needed to switch from ncp to GNU mode on Engine Load
\r
7830 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7831 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7832 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7833 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7834 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7835 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7836 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7837 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7838 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7840 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7841 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7842 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7843 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7847 Enables icsEnables[] = {
\r
7848 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7871 Enables zippyEnables[] = {
\r
7872 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7880 Enables ncpEnables[] = {
\r
7881 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7891 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7906 Enables trainingOnEnables[] = {
\r
7907 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7919 Enables trainingOffEnables[] = {
\r
7920 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7932 /* These modify either ncpEnables or gnuEnables */
\r
7933 Enables cmailEnables[] = {
\r
7934 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7936 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7937 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7944 Enables machineThinkingEnables[] = {
\r
7945 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7958 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7964 Enables userThinkingEnables[] = {
\r
7965 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7966 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7967 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7968 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7969 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7970 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7971 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7972 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7973 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7975 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7976 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7977 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7978 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7984 /*---------------------------------------------------------------------------*\
\r
7986 * Front-end interface functions exported by XBoard.
\r
7987 * Functions appear in same order as prototypes in frontend.h.
\r
7989 \*---------------------------------------------------------------------------*/
\r
7991 CheckMark(UINT item, int state)
\r
7993 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7999 static UINT prevChecked = 0;
\r
8000 static int prevPausing = 0;
\r
8003 if (pausing != prevPausing) {
\r
8004 prevPausing = pausing;
\r
8005 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8006 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8007 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8010 switch (gameMode) {
\r
8011 case BeginningOfGame:
\r
8012 if (appData.icsActive)
\r
8013 nowChecked = IDM_IcsClient;
\r
8014 else if (appData.noChessProgram)
\r
8015 nowChecked = IDM_EditGame;
\r
8017 nowChecked = IDM_MachineBlack;
\r
8019 case MachinePlaysBlack:
\r
8020 nowChecked = IDM_MachineBlack;
\r
8022 case MachinePlaysWhite:
\r
8023 nowChecked = IDM_MachineWhite;
\r
8025 case TwoMachinesPlay:
\r
8026 nowChecked = IDM_TwoMachines;
\r
8029 nowChecked = IDM_AnalysisMode;
\r
8032 nowChecked = IDM_AnalyzeFile;
\r
8035 nowChecked = IDM_EditGame;
\r
8037 case PlayFromGameFile:
\r
8038 nowChecked = IDM_LoadGame;
\r
8040 case EditPosition:
\r
8041 nowChecked = IDM_EditPosition;
\r
8044 nowChecked = IDM_Training;
\r
8046 case IcsPlayingWhite:
\r
8047 case IcsPlayingBlack:
\r
8048 case IcsObserving:
\r
8050 nowChecked = IDM_IcsClient;
\r
8057 CheckMark(prevChecked, MF_UNCHECKED);
\r
8058 CheckMark(nowChecked, MF_CHECKED);
\r
8059 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8061 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8062 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8063 MF_BYCOMMAND|MF_ENABLED);
\r
8065 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8066 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8069 prevChecked = nowChecked;
\r
8071 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8072 if (appData.icsActive) {
\r
8073 if (appData.icsEngineAnalyze) {
\r
8074 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8076 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8079 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8085 HMENU hmenu = GetMenu(hwndMain);
\r
8086 SetMenuEnables(hmenu, icsEnables);
\r
8087 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8088 MF_BYCOMMAND|MF_ENABLED);
\r
8090 if (appData.zippyPlay) {
\r
8091 SetMenuEnables(hmenu, zippyEnables);
\r
8092 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8093 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8094 MF_BYCOMMAND|MF_ENABLED);
\r
8102 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8108 HMENU hmenu = GetMenu(hwndMain);
\r
8109 SetMenuEnables(hmenu, ncpEnables);
\r
8110 DrawMenuBar(hwndMain);
\r
8116 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8120 SetTrainingModeOn()
\r
8123 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8124 for (i = 0; i < N_BUTTONS; i++) {
\r
8125 if (buttonDesc[i].hwnd != NULL)
\r
8126 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8131 VOID SetTrainingModeOff()
\r
8134 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8135 for (i = 0; i < N_BUTTONS; i++) {
\r
8136 if (buttonDesc[i].hwnd != NULL)
\r
8137 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8143 SetUserThinkingEnables()
\r
8145 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8149 SetMachineThinkingEnables()
\r
8151 HMENU hMenu = GetMenu(hwndMain);
\r
8152 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8154 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8156 if (gameMode == MachinePlaysBlack) {
\r
8157 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8158 } else if (gameMode == MachinePlaysWhite) {
\r
8159 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8160 } else if (gameMode == TwoMachinesPlay) {
\r
8161 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8167 DisplayTitle(char *str)
\r
8169 char title[MSG_SIZ], *host;
\r
8170 if (str[0] != NULLCHAR) {
\r
8171 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8172 } else if (appData.icsActive) {
\r
8173 if (appData.icsCommPort[0] != NULLCHAR)
\r
8176 host = appData.icsHost;
\r
8177 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8178 } else if (appData.noChessProgram) {
\r
8179 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8181 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8182 strcat(title, ": ");
\r
8183 strcat(title, first.tidy);
\r
8185 SetWindowText(hwndMain, title);
\r
8190 DisplayMessage(char *str1, char *str2)
\r
8194 int remain = MESSAGE_TEXT_MAX - 1;
\r
8197 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8198 messageText[0] = NULLCHAR;
\r
8200 len = strlen(str1);
\r
8201 if (len > remain) len = remain;
\r
8202 strncpy(messageText, str1, len);
\r
8203 messageText[len] = NULLCHAR;
\r
8206 if (*str2 && remain >= 2) {
\r
8208 strcat(messageText, " ");
\r
8211 len = strlen(str2);
\r
8212 if (len > remain) len = remain;
\r
8213 strncat(messageText, str2, len);
\r
8215 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8216 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8218 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8222 hdc = GetDC(hwndMain);
\r
8223 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8224 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8225 &messageRect, messageText, strlen(messageText), NULL);
\r
8226 (void) SelectObject(hdc, oldFont);
\r
8227 (void) ReleaseDC(hwndMain, hdc);
\r
8231 DisplayError(char *str, int error)
\r
8233 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8237 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8239 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8240 NULL, error, LANG_NEUTRAL,
\r
8241 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8243 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8245 ErrorMap *em = errmap;
\r
8246 while (em->err != 0 && em->err != error) em++;
\r
8247 if (em->err != 0) {
\r
8248 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8250 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8255 ErrorPopUp(_("Error"), buf);
\r
8260 DisplayMoveError(char *str)
\r
8262 fromX = fromY = -1;
\r
8263 ClearHighlights();
\r
8264 DrawPosition(FALSE, NULL);
\r
8265 if (appData.popupMoveErrors) {
\r
8266 ErrorPopUp(_("Error"), str);
\r
8268 DisplayMessage(str, "");
\r
8269 moveErrorMessageUp = TRUE;
\r
8274 DisplayFatalError(char *str, int error, int exitStatus)
\r
8276 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8278 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8281 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8282 NULL, error, LANG_NEUTRAL,
\r
8283 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8285 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8287 ErrorMap *em = errmap;
\r
8288 while (em->err != 0 && em->err != error) em++;
\r
8289 if (em->err != 0) {
\r
8290 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8292 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8297 if (appData.debugMode) {
\r
8298 fprintf(debugFP, "%s: %s\n", label, str);
\r
8300 if (appData.popupExitMessage) {
\r
8301 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8302 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8304 ExitEvent(exitStatus);
\r
8309 DisplayInformation(char *str)
\r
8311 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8316 DisplayNote(char *str)
\r
8318 ErrorPopUp(_("Note"), str);
\r
8323 char *title, *question, *replyPrefix;
\r
8328 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8330 static QuestionParams *qp;
\r
8331 char reply[MSG_SIZ];
\r
8334 switch (message) {
\r
8335 case WM_INITDIALOG:
\r
8336 qp = (QuestionParams *) lParam;
\r
8337 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8338 Translate(hDlg, DLG_Question);
\r
8339 SetWindowText(hDlg, qp->title);
\r
8340 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8341 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8345 switch (LOWORD(wParam)) {
\r
8347 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8348 if (*reply) strcat(reply, " ");
\r
8349 len = strlen(reply);
\r
8350 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8351 strcat(reply, "\n");
\r
8352 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8353 EndDialog(hDlg, TRUE);
\r
8354 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8357 EndDialog(hDlg, FALSE);
\r
8368 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8370 QuestionParams qp;
\r
8374 qp.question = question;
\r
8375 qp.replyPrefix = replyPrefix;
\r
8377 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8378 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8379 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8380 FreeProcInstance(lpProc);
\r
8383 /* [AS] Pick FRC position */
\r
8384 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8386 static int * lpIndexFRC;
\r
8392 case WM_INITDIALOG:
\r
8393 lpIndexFRC = (int *) lParam;
\r
8395 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8396 Translate(hDlg, DLG_NewGameFRC);
\r
8398 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8399 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8400 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8401 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8406 switch( LOWORD(wParam) ) {
\r
8408 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8409 EndDialog( hDlg, 0 );
\r
8410 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8413 EndDialog( hDlg, 1 );
\r
8415 case IDC_NFG_Edit:
\r
8416 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8417 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8419 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8422 case IDC_NFG_Random:
\r
8423 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8424 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8437 int index = appData.defaultFrcPosition;
\r
8438 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8440 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8442 if( result == 0 ) {
\r
8443 appData.defaultFrcPosition = index;
\r
8449 /* [AS] Game list options. Refactored by HGM */
\r
8451 HWND gameListOptionsDialog;
\r
8453 // low-level front-end: clear text edit / list widget
\r
8457 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8460 // low-level front-end: clear text edit / list widget
\r
8462 GLT_DeSelectList()
\r
8464 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8467 // low-level front-end: append line to text edit / list widget
\r
8469 GLT_AddToList( char *name )
\r
8472 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8476 // low-level front-end: get line from text edit / list widget
\r
8478 GLT_GetFromList( int index, char *name )
\r
8481 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8487 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8489 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8490 int idx2 = idx1 + delta;
\r
8491 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8493 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8496 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8497 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8498 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8499 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8503 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8507 case WM_INITDIALOG:
\r
8508 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8510 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8511 Translate(hDlg, DLG_GameListOptions);
\r
8513 /* Initialize list */
\r
8514 GLT_TagsToList( lpUserGLT );
\r
8516 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8521 switch( LOWORD(wParam) ) {
\r
8524 EndDialog( hDlg, 0 );
\r
8527 EndDialog( hDlg, 1 );
\r
8530 case IDC_GLT_Default:
\r
8531 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8534 case IDC_GLT_Restore:
\r
8535 GLT_TagsToList( appData.gameListTags );
\r
8539 GLT_MoveSelection( hDlg, -1 );
\r
8542 case IDC_GLT_Down:
\r
8543 GLT_MoveSelection( hDlg, +1 );
\r
8553 int GameListOptions()
\r
8556 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8558 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8560 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8562 if( result == 0 ) {
\r
8563 /* [AS] Memory leak here! */
\r
8564 appData.gameListTags = strdup( lpUserGLT );
\r
8571 DisplayIcsInteractionTitle(char *str)
\r
8573 char consoleTitle[MSG_SIZ];
\r
8575 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8576 SetWindowText(hwndConsole, consoleTitle);
\r
8578 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8579 char buf[MSG_SIZ], *p = buf, *q;
\r
8580 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8582 q = strchr(p, ';');
\r
8584 if(*p) ChatPopUp(p);
\r
8588 SetActiveWindow(hwndMain);
\r
8592 DrawPosition(int fullRedraw, Board board)
\r
8594 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8597 void NotifyFrontendLogin()
\r
8600 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8606 fromX = fromY = -1;
\r
8607 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8608 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8609 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8610 dragInfo.lastpos = dragInfo.pos;
\r
8611 dragInfo.start.x = dragInfo.start.y = -1;
\r
8612 dragInfo.from = dragInfo.start;
\r
8614 DrawPosition(TRUE, NULL);
\r
8621 CommentPopUp(char *title, char *str)
\r
8623 HWND hwnd = GetActiveWindow();
\r
8624 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8626 SetActiveWindow(hwnd);
\r
8630 CommentPopDown(void)
\r
8632 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8633 if (commentDialog) {
\r
8634 ShowWindow(commentDialog, SW_HIDE);
\r
8636 commentUp = FALSE;
\r
8640 EditCommentPopUp(int index, char *title, char *str)
\r
8642 EitherCommentPopUp(index, title, str, TRUE);
\r
8649 MyPlaySound(&sounds[(int)SoundMove]);
\r
8652 VOID PlayIcsWinSound()
\r
8654 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8657 VOID PlayIcsLossSound()
\r
8659 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8662 VOID PlayIcsDrawSound()
\r
8664 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8667 VOID PlayIcsUnfinishedSound()
\r
8669 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8675 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8681 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8689 consoleEcho = TRUE;
\r
8690 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8691 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8692 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8701 consoleEcho = FALSE;
\r
8702 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8703 /* This works OK: set text and background both to the same color */
\r
8705 cf.crTextColor = COLOR_ECHOOFF;
\r
8706 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8707 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8710 /* No Raw()...? */
\r
8712 void Colorize(ColorClass cc, int continuation)
\r
8714 currentColorClass = cc;
\r
8715 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8716 consoleCF.crTextColor = textAttribs[cc].color;
\r
8717 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8718 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8724 static char buf[MSG_SIZ];
\r
8725 DWORD bufsiz = MSG_SIZ;
\r
8727 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8728 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8730 if (!GetUserName(buf, &bufsiz)) {
\r
8731 /*DisplayError("Error getting user name", GetLastError());*/
\r
8732 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8740 static char buf[MSG_SIZ];
\r
8741 DWORD bufsiz = MSG_SIZ;
\r
8743 if (!GetComputerName(buf, &bufsiz)) {
\r
8744 /*DisplayError("Error getting host name", GetLastError());*/
\r
8745 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8752 ClockTimerRunning()
\r
8754 return clockTimerEvent != 0;
\r
8760 if (clockTimerEvent == 0) return FALSE;
\r
8761 KillTimer(hwndMain, clockTimerEvent);
\r
8762 clockTimerEvent = 0;
\r
8767 StartClockTimer(long millisec)
\r
8769 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8770 (UINT) millisec, NULL);
\r
8774 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8777 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8779 if(appData.noGUI) return;
\r
8780 hdc = GetDC(hwndMain);
\r
8781 if (!IsIconic(hwndMain)) {
\r
8782 DisplayAClock(hdc, timeRemaining, highlight,
\r
8783 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8785 if (highlight && iconCurrent == iconBlack) {
\r
8786 iconCurrent = iconWhite;
\r
8787 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8788 if (IsIconic(hwndMain)) {
\r
8789 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8792 (void) ReleaseDC(hwndMain, hdc);
\r
8794 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8798 DisplayBlackClock(long timeRemaining, int highlight)
\r
8801 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8803 if(appData.noGUI) return;
\r
8804 hdc = GetDC(hwndMain);
\r
8805 if (!IsIconic(hwndMain)) {
\r
8806 DisplayAClock(hdc, timeRemaining, highlight,
\r
8807 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8809 if (highlight && iconCurrent == iconWhite) {
\r
8810 iconCurrent = iconBlack;
\r
8811 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8812 if (IsIconic(hwndMain)) {
\r
8813 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8816 (void) ReleaseDC(hwndMain, hdc);
\r
8818 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8823 LoadGameTimerRunning()
\r
8825 return loadGameTimerEvent != 0;
\r
8829 StopLoadGameTimer()
\r
8831 if (loadGameTimerEvent == 0) return FALSE;
\r
8832 KillTimer(hwndMain, loadGameTimerEvent);
\r
8833 loadGameTimerEvent = 0;
\r
8838 StartLoadGameTimer(long millisec)
\r
8840 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8841 (UINT) millisec, NULL);
\r
8849 char fileTitle[MSG_SIZ];
\r
8851 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8852 f = OpenFileDialog(hwndMain, "a", defName,
\r
8853 appData.oldSaveStyle ? "gam" : "pgn",
\r
8855 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8857 SaveGame(f, 0, "");
\r
8864 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8866 if (delayedTimerEvent != 0) {
\r
8867 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8868 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8870 KillTimer(hwndMain, delayedTimerEvent);
\r
8871 delayedTimerEvent = 0;
\r
8872 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8873 delayedTimerCallback();
\r
8875 delayedTimerCallback = cb;
\r
8876 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8877 (UINT) millisec, NULL);
\r
8880 DelayedEventCallback
\r
8883 if (delayedTimerEvent) {
\r
8884 return delayedTimerCallback;
\r
8891 CancelDelayedEvent()
\r
8893 if (delayedTimerEvent) {
\r
8894 KillTimer(hwndMain, delayedTimerEvent);
\r
8895 delayedTimerEvent = 0;
\r
8899 DWORD GetWin32Priority(int nice)
\r
8900 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8902 REALTIME_PRIORITY_CLASS 0x00000100
\r
8903 HIGH_PRIORITY_CLASS 0x00000080
\r
8904 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8905 NORMAL_PRIORITY_CLASS 0x00000020
\r
8906 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8907 IDLE_PRIORITY_CLASS 0x00000040
\r
8909 if (nice < -15) return 0x00000080;
\r
8910 if (nice < 0) return 0x00008000;
\r
8911 if (nice == 0) return 0x00000020;
\r
8912 if (nice < 15) return 0x00004000;
\r
8913 return 0x00000040;
\r
8916 void RunCommand(char *cmdLine)
\r
8918 /* Now create the child process. */
\r
8919 STARTUPINFO siStartInfo;
\r
8920 PROCESS_INFORMATION piProcInfo;
\r
8922 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8923 siStartInfo.lpReserved = NULL;
\r
8924 siStartInfo.lpDesktop = NULL;
\r
8925 siStartInfo.lpTitle = NULL;
\r
8926 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8927 siStartInfo.cbReserved2 = 0;
\r
8928 siStartInfo.lpReserved2 = NULL;
\r
8929 siStartInfo.hStdInput = NULL;
\r
8930 siStartInfo.hStdOutput = NULL;
\r
8931 siStartInfo.hStdError = NULL;
\r
8933 CreateProcess(NULL,
\r
8934 cmdLine, /* command line */
\r
8935 NULL, /* process security attributes */
\r
8936 NULL, /* primary thread security attrs */
\r
8937 TRUE, /* handles are inherited */
\r
8938 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8939 NULL, /* use parent's environment */
\r
8941 &siStartInfo, /* STARTUPINFO pointer */
\r
8942 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8944 CloseHandle(piProcInfo.hThread);
\r
8947 /* Start a child process running the given program.
\r
8948 The process's standard output can be read from "from", and its
\r
8949 standard input can be written to "to".
\r
8950 Exit with fatal error if anything goes wrong.
\r
8951 Returns an opaque pointer that can be used to destroy the process
\r
8955 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8957 #define BUFSIZE 4096
\r
8959 HANDLE hChildStdinRd, hChildStdinWr,
\r
8960 hChildStdoutRd, hChildStdoutWr;
\r
8961 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8962 SECURITY_ATTRIBUTES saAttr;
\r
8964 PROCESS_INFORMATION piProcInfo;
\r
8965 STARTUPINFO siStartInfo;
\r
8967 char buf[MSG_SIZ];
\r
8970 if (appData.debugMode) {
\r
8971 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8976 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8977 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8978 saAttr.bInheritHandle = TRUE;
\r
8979 saAttr.lpSecurityDescriptor = NULL;
\r
8982 * The steps for redirecting child's STDOUT:
\r
8983 * 1. Create anonymous pipe to be STDOUT for child.
\r
8984 * 2. Create a noninheritable duplicate of read handle,
\r
8985 * and close the inheritable read handle.
\r
8988 /* Create a pipe for the child's STDOUT. */
\r
8989 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8990 return GetLastError();
\r
8993 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8994 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8995 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8996 FALSE, /* not inherited */
\r
8997 DUPLICATE_SAME_ACCESS);
\r
8999 return GetLastError();
\r
9001 CloseHandle(hChildStdoutRd);
\r
9004 * The steps for redirecting child's STDIN:
\r
9005 * 1. Create anonymous pipe to be STDIN for child.
\r
9006 * 2. Create a noninheritable duplicate of write handle,
\r
9007 * and close the inheritable write handle.
\r
9010 /* Create a pipe for the child's STDIN. */
\r
9011 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9012 return GetLastError();
\r
9015 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9016 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9017 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9018 FALSE, /* not inherited */
\r
9019 DUPLICATE_SAME_ACCESS);
\r
9021 return GetLastError();
\r
9023 CloseHandle(hChildStdinWr);
\r
9025 /* Arrange to (1) look in dir for the child .exe file, and
\r
9026 * (2) have dir be the child's working directory. Interpret
\r
9027 * dir relative to the directory WinBoard loaded from. */
\r
9028 GetCurrentDirectory(MSG_SIZ, buf);
\r
9029 SetCurrentDirectory(installDir);
\r
9030 SetCurrentDirectory(dir);
\r
9032 /* Now create the child process. */
\r
9034 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9035 siStartInfo.lpReserved = NULL;
\r
9036 siStartInfo.lpDesktop = NULL;
\r
9037 siStartInfo.lpTitle = NULL;
\r
9038 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9039 siStartInfo.cbReserved2 = 0;
\r
9040 siStartInfo.lpReserved2 = NULL;
\r
9041 siStartInfo.hStdInput = hChildStdinRd;
\r
9042 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9043 siStartInfo.hStdError = hChildStdoutWr;
\r
9045 fSuccess = CreateProcess(NULL,
\r
9046 cmdLine, /* command line */
\r
9047 NULL, /* process security attributes */
\r
9048 NULL, /* primary thread security attrs */
\r
9049 TRUE, /* handles are inherited */
\r
9050 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9051 NULL, /* use parent's environment */
\r
9053 &siStartInfo, /* STARTUPINFO pointer */
\r
9054 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9056 err = GetLastError();
\r
9057 SetCurrentDirectory(buf); /* return to prev directory */
\r
9062 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9063 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9064 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9067 /* Close the handles we don't need in the parent */
\r
9068 CloseHandle(piProcInfo.hThread);
\r
9069 CloseHandle(hChildStdinRd);
\r
9070 CloseHandle(hChildStdoutWr);
\r
9072 /* Prepare return value */
\r
9073 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9074 cp->kind = CPReal;
\r
9075 cp->hProcess = piProcInfo.hProcess;
\r
9076 cp->pid = piProcInfo.dwProcessId;
\r
9077 cp->hFrom = hChildStdoutRdDup;
\r
9078 cp->hTo = hChildStdinWrDup;
\r
9080 *pr = (void *) cp;
\r
9082 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9083 2000 where engines sometimes don't see the initial command(s)
\r
9084 from WinBoard and hang. I don't understand how that can happen,
\r
9085 but the Sleep is harmless, so I've put it in. Others have also
\r
9086 reported what may be the same problem, so hopefully this will fix
\r
9087 it for them too. */
\r
9095 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9097 ChildProc *cp; int result;
\r
9099 cp = (ChildProc *) pr;
\r
9100 if (cp == NULL) return;
\r
9102 switch (cp->kind) {
\r
9104 /* TerminateProcess is considered harmful, so... */
\r
9105 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9106 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9107 /* The following doesn't work because the chess program
\r
9108 doesn't "have the same console" as WinBoard. Maybe
\r
9109 we could arrange for this even though neither WinBoard
\r
9110 nor the chess program uses a console for stdio? */
\r
9111 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9113 /* [AS] Special termination modes for misbehaving programs... */
\r
9114 if( signal == 9 ) {
\r
9115 result = TerminateProcess( cp->hProcess, 0 );
\r
9117 if ( appData.debugMode) {
\r
9118 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9121 else if( signal == 10 ) {
\r
9122 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9124 if( dw != WAIT_OBJECT_0 ) {
\r
9125 result = TerminateProcess( cp->hProcess, 0 );
\r
9127 if ( appData.debugMode) {
\r
9128 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9134 CloseHandle(cp->hProcess);
\r
9138 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9142 closesocket(cp->sock);
\r
9147 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9148 closesocket(cp->sock);
\r
9149 closesocket(cp->sock2);
\r
9157 InterruptChildProcess(ProcRef pr)
\r
9161 cp = (ChildProc *) pr;
\r
9162 if (cp == NULL) return;
\r
9163 switch (cp->kind) {
\r
9165 /* The following doesn't work because the chess program
\r
9166 doesn't "have the same console" as WinBoard. Maybe
\r
9167 we could arrange for this even though neither WinBoard
\r
9168 nor the chess program uses a console for stdio */
\r
9169 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9174 /* Can't interrupt */
\r
9178 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9185 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9187 char cmdLine[MSG_SIZ];
\r
9189 if (port[0] == NULLCHAR) {
\r
9190 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9192 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9194 return StartChildProcess(cmdLine, "", pr);
\r
9198 /* Code to open TCP sockets */
\r
9201 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9206 struct sockaddr_in sa, mysa;
\r
9207 struct hostent FAR *hp;
\r
9208 unsigned short uport;
\r
9209 WORD wVersionRequested;
\r
9212 /* Initialize socket DLL */
\r
9213 wVersionRequested = MAKEWORD(1, 1);
\r
9214 err = WSAStartup(wVersionRequested, &wsaData);
\r
9215 if (err != 0) return err;
\r
9218 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9219 err = WSAGetLastError();
\r
9224 /* Bind local address using (mostly) don't-care values.
\r
9226 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9227 mysa.sin_family = AF_INET;
\r
9228 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9229 uport = (unsigned short) 0;
\r
9230 mysa.sin_port = htons(uport);
\r
9231 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9232 == SOCKET_ERROR) {
\r
9233 err = WSAGetLastError();
\r
9238 /* Resolve remote host name */
\r
9239 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9240 if (!(hp = gethostbyname(host))) {
\r
9241 unsigned int b0, b1, b2, b3;
\r
9243 err = WSAGetLastError();
\r
9245 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9246 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9247 hp->h_addrtype = AF_INET;
\r
9249 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9250 hp->h_addr_list[0] = (char *) malloc(4);
\r
9251 hp->h_addr_list[0][0] = (char) b0;
\r
9252 hp->h_addr_list[0][1] = (char) b1;
\r
9253 hp->h_addr_list[0][2] = (char) b2;
\r
9254 hp->h_addr_list[0][3] = (char) b3;
\r
9260 sa.sin_family = hp->h_addrtype;
\r
9261 uport = (unsigned short) atoi(port);
\r
9262 sa.sin_port = htons(uport);
\r
9263 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9265 /* Make connection */
\r
9266 if (connect(s, (struct sockaddr *) &sa,
\r
9267 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9268 err = WSAGetLastError();
\r
9273 /* Prepare return value */
\r
9274 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9275 cp->kind = CPSock;
\r
9277 *pr = (ProcRef *) cp;
\r
9283 OpenCommPort(char *name, ProcRef *pr)
\r
9288 char fullname[MSG_SIZ];
\r
9290 if (*name != '\\')
\r
9291 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9293 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9295 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9296 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9297 if (h == (HANDLE) -1) {
\r
9298 return GetLastError();
\r
9302 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9304 /* Accumulate characters until a 100ms pause, then parse */
\r
9305 ct.ReadIntervalTimeout = 100;
\r
9306 ct.ReadTotalTimeoutMultiplier = 0;
\r
9307 ct.ReadTotalTimeoutConstant = 0;
\r
9308 ct.WriteTotalTimeoutMultiplier = 0;
\r
9309 ct.WriteTotalTimeoutConstant = 0;
\r
9310 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9312 /* Prepare return value */
\r
9313 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9314 cp->kind = CPComm;
\r
9317 *pr = (ProcRef *) cp;
\r
9323 OpenLoopback(ProcRef *pr)
\r
9325 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9331 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9336 struct sockaddr_in sa, mysa;
\r
9337 struct hostent FAR *hp;
\r
9338 unsigned short uport;
\r
9339 WORD wVersionRequested;
\r
9342 char stderrPortStr[MSG_SIZ];
\r
9344 /* Initialize socket DLL */
\r
9345 wVersionRequested = MAKEWORD(1, 1);
\r
9346 err = WSAStartup(wVersionRequested, &wsaData);
\r
9347 if (err != 0) return err;
\r
9349 /* Resolve remote host name */
\r
9350 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9351 if (!(hp = gethostbyname(host))) {
\r
9352 unsigned int b0, b1, b2, b3;
\r
9354 err = WSAGetLastError();
\r
9356 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9357 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9358 hp->h_addrtype = AF_INET;
\r
9360 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9361 hp->h_addr_list[0] = (char *) malloc(4);
\r
9362 hp->h_addr_list[0][0] = (char) b0;
\r
9363 hp->h_addr_list[0][1] = (char) b1;
\r
9364 hp->h_addr_list[0][2] = (char) b2;
\r
9365 hp->h_addr_list[0][3] = (char) b3;
\r
9371 sa.sin_family = hp->h_addrtype;
\r
9372 uport = (unsigned short) 514;
\r
9373 sa.sin_port = htons(uport);
\r
9374 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9376 /* Bind local socket to unused "privileged" port address
\r
9378 s = INVALID_SOCKET;
\r
9379 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9380 mysa.sin_family = AF_INET;
\r
9381 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9382 for (fromPort = 1023;; fromPort--) {
\r
9383 if (fromPort < 0) {
\r
9385 return WSAEADDRINUSE;
\r
9387 if (s == INVALID_SOCKET) {
\r
9388 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9389 err = WSAGetLastError();
\r
9394 uport = (unsigned short) fromPort;
\r
9395 mysa.sin_port = htons(uport);
\r
9396 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9397 == SOCKET_ERROR) {
\r
9398 err = WSAGetLastError();
\r
9399 if (err == WSAEADDRINUSE) continue;
\r
9403 if (connect(s, (struct sockaddr *) &sa,
\r
9404 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9405 err = WSAGetLastError();
\r
9406 if (err == WSAEADDRINUSE) {
\r
9417 /* Bind stderr local socket to unused "privileged" port address
\r
9419 s2 = INVALID_SOCKET;
\r
9420 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9421 mysa.sin_family = AF_INET;
\r
9422 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9423 for (fromPort = 1023;; fromPort--) {
\r
9424 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9425 if (fromPort < 0) {
\r
9426 (void) closesocket(s);
\r
9428 return WSAEADDRINUSE;
\r
9430 if (s2 == INVALID_SOCKET) {
\r
9431 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9432 err = WSAGetLastError();
\r
9438 uport = (unsigned short) fromPort;
\r
9439 mysa.sin_port = htons(uport);
\r
9440 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9441 == SOCKET_ERROR) {
\r
9442 err = WSAGetLastError();
\r
9443 if (err == WSAEADDRINUSE) continue;
\r
9444 (void) closesocket(s);
\r
9448 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9449 err = WSAGetLastError();
\r
9450 if (err == WSAEADDRINUSE) {
\r
9452 s2 = INVALID_SOCKET;
\r
9455 (void) closesocket(s);
\r
9456 (void) closesocket(s2);
\r
9462 prevStderrPort = fromPort; // remember port used
\r
9463 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9465 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9466 err = WSAGetLastError();
\r
9467 (void) closesocket(s);
\r
9468 (void) closesocket(s2);
\r
9473 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9474 err = WSAGetLastError();
\r
9475 (void) closesocket(s);
\r
9476 (void) closesocket(s2);
\r
9480 if (*user == NULLCHAR) user = UserName();
\r
9481 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9482 err = WSAGetLastError();
\r
9483 (void) closesocket(s);
\r
9484 (void) closesocket(s2);
\r
9488 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9489 err = WSAGetLastError();
\r
9490 (void) closesocket(s);
\r
9491 (void) closesocket(s2);
\r
9496 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9497 err = WSAGetLastError();
\r
9498 (void) closesocket(s);
\r
9499 (void) closesocket(s2);
\r
9503 (void) closesocket(s2); /* Stop listening */
\r
9505 /* Prepare return value */
\r
9506 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9507 cp->kind = CPRcmd;
\r
9510 *pr = (ProcRef *) cp;
\r
9517 AddInputSource(ProcRef pr, int lineByLine,
\r
9518 InputCallback func, VOIDSTAR closure)
\r
9520 InputSource *is, *is2 = NULL;
\r
9521 ChildProc *cp = (ChildProc *) pr;
\r
9523 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9524 is->lineByLine = lineByLine;
\r
9526 is->closure = closure;
\r
9527 is->second = NULL;
\r
9528 is->next = is->buf;
\r
9529 if (pr == NoProc) {
\r
9530 is->kind = CPReal;
\r
9531 consoleInputSource = is;
\r
9533 is->kind = cp->kind;
\r
9535 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9536 we create all threads suspended so that the is->hThread variable can be
\r
9537 safely assigned, then let the threads start with ResumeThread.
\r
9539 switch (cp->kind) {
\r
9541 is->hFile = cp->hFrom;
\r
9542 cp->hFrom = NULL; /* now owned by InputThread */
\r
9544 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9545 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9549 is->hFile = cp->hFrom;
\r
9550 cp->hFrom = NULL; /* now owned by InputThread */
\r
9552 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9553 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9557 is->sock = cp->sock;
\r
9559 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9560 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9564 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9566 is->sock = cp->sock;
\r
9568 is2->sock = cp->sock2;
\r
9569 is2->second = is2;
\r
9571 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9572 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9574 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9575 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9579 if( is->hThread != NULL ) {
\r
9580 ResumeThread( is->hThread );
\r
9583 if( is2 != NULL && is2->hThread != NULL ) {
\r
9584 ResumeThread( is2->hThread );
\r
9588 return (InputSourceRef) is;
\r
9592 RemoveInputSource(InputSourceRef isr)
\r
9596 is = (InputSource *) isr;
\r
9597 is->hThread = NULL; /* tell thread to stop */
\r
9598 CloseHandle(is->hThread);
\r
9599 if (is->second != NULL) {
\r
9600 is->second->hThread = NULL;
\r
9601 CloseHandle(is->second->hThread);
\r
9605 int no_wrap(char *message, int count)
\r
9607 ConsoleOutput(message, count, FALSE);
\r
9612 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9615 int outCount = SOCKET_ERROR;
\r
9616 ChildProc *cp = (ChildProc *) pr;
\r
9617 static OVERLAPPED ovl;
\r
9618 static int line = 0;
\r
9622 if (appData.noJoin || !appData.useInternalWrap)
\r
9623 return no_wrap(message, count);
\r
9626 int width = get_term_width();
\r
9627 int len = wrap(NULL, message, count, width, &line);
\r
9628 char *msg = malloc(len);
\r
9632 return no_wrap(message, count);
\r
9635 dbgchk = wrap(msg, message, count, width, &line);
\r
9636 if (dbgchk != len && appData.debugMode)
\r
9637 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9638 ConsoleOutput(msg, len, FALSE);
\r
9645 if (ovl.hEvent == NULL) {
\r
9646 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9648 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9650 switch (cp->kind) {
\r
9653 outCount = send(cp->sock, message, count, 0);
\r
9654 if (outCount == SOCKET_ERROR) {
\r
9655 *outError = WSAGetLastError();
\r
9657 *outError = NO_ERROR;
\r
9662 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9663 &dOutCount, NULL)) {
\r
9664 *outError = NO_ERROR;
\r
9665 outCount = (int) dOutCount;
\r
9667 *outError = GetLastError();
\r
9672 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9673 &dOutCount, &ovl);
\r
9674 if (*outError == NO_ERROR) {
\r
9675 outCount = (int) dOutCount;
\r
9685 if(n != 0) Sleep(n);
\r
9689 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9692 /* Ignore delay, not implemented for WinBoard */
\r
9693 return OutputToProcess(pr, message, count, outError);
\r
9698 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9699 char *buf, int count, int error)
\r
9701 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9704 /* see wgamelist.c for Game List functions */
\r
9705 /* see wedittags.c for Edit Tags functions */
\r
9712 char buf[MSG_SIZ];
\r
9715 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9716 f = fopen(buf, "r");
\r
9718 ProcessICSInitScript(f);
\r
9726 StartAnalysisClock()
\r
9728 if (analysisTimerEvent) return;
\r
9729 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9730 (UINT) 2000, NULL);
\r
9734 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9736 highlightInfo.sq[0].x = fromX;
\r
9737 highlightInfo.sq[0].y = fromY;
\r
9738 highlightInfo.sq[1].x = toX;
\r
9739 highlightInfo.sq[1].y = toY;
\r
9745 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9746 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9750 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9752 premoveHighlightInfo.sq[0].x = fromX;
\r
9753 premoveHighlightInfo.sq[0].y = fromY;
\r
9754 premoveHighlightInfo.sq[1].x = toX;
\r
9755 premoveHighlightInfo.sq[1].y = toY;
\r
9759 ClearPremoveHighlights()
\r
9761 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9762 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9766 ShutDownFrontEnd()
\r
9768 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9769 DeleteClipboardTempFiles();
\r
9775 if (IsIconic(hwndMain))
\r
9776 ShowWindow(hwndMain, SW_RESTORE);
\r
9778 SetActiveWindow(hwndMain);
\r
9782 * Prototypes for animation support routines
\r
9784 static void ScreenSquare(int column, int row, POINT * pt);
\r
9785 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9786 POINT frames[], int * nFrames);
\r
9792 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9793 { // [HGM] atomic: animate blast wave
\r
9796 explodeInfo.fromX = fromX;
\r
9797 explodeInfo.fromY = fromY;
\r
9798 explodeInfo.toX = toX;
\r
9799 explodeInfo.toY = toY;
\r
9800 for(i=1; i<4*kFactor; i++) {
\r
9801 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9802 DrawPosition(FALSE, board);
\r
9803 Sleep(appData.animSpeed);
\r
9805 explodeInfo.radius = 0;
\r
9806 DrawPosition(TRUE, board);
\r
9810 AnimateMove(board, fromX, fromY, toX, toY)
\r
9817 ChessSquare piece;
\r
9818 POINT start, finish, mid;
\r
9819 POINT frames[kFactor * 2 + 1];
\r
9822 if (!appData.animate) return;
\r
9823 if (doingSizing) return;
\r
9824 if (fromY < 0 || fromX < 0) return;
\r
9825 piece = board[fromY][fromX];
\r
9826 if (piece >= EmptySquare) return;
\r
9828 ScreenSquare(fromX, fromY, &start);
\r
9829 ScreenSquare(toX, toY, &finish);
\r
9831 /* All moves except knight jumps move in straight line */
\r
9832 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9833 mid.x = start.x + (finish.x - start.x) / 2;
\r
9834 mid.y = start.y + (finish.y - start.y) / 2;
\r
9836 /* Knight: make straight movement then diagonal */
\r
9837 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9838 mid.x = start.x + (finish.x - start.x) / 2;
\r
9842 mid.y = start.y + (finish.y - start.y) / 2;
\r
9846 /* Don't use as many frames for very short moves */
\r
9847 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9848 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9850 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9852 animInfo.from.x = fromX;
\r
9853 animInfo.from.y = fromY;
\r
9854 animInfo.to.x = toX;
\r
9855 animInfo.to.y = toY;
\r
9856 animInfo.lastpos = start;
\r
9857 animInfo.piece = piece;
\r
9858 for (n = 0; n < nFrames; n++) {
\r
9859 animInfo.pos = frames[n];
\r
9860 DrawPosition(FALSE, NULL);
\r
9861 animInfo.lastpos = animInfo.pos;
\r
9862 Sleep(appData.animSpeed);
\r
9864 animInfo.pos = finish;
\r
9865 DrawPosition(FALSE, NULL);
\r
9866 animInfo.piece = EmptySquare;
\r
9867 Explode(board, fromX, fromY, toX, toY);
\r
9870 /* Convert board position to corner of screen rect and color */
\r
9873 ScreenSquare(column, row, pt)
\r
9874 int column; int row; POINT * pt;
\r
9877 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9878 pt->y = lineGap + row * (squareSize + lineGap);
\r
9880 pt->x = lineGap + column * (squareSize + lineGap);
\r
9881 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9885 /* Generate a series of frame coords from start->mid->finish.
\r
9886 The movement rate doubles until the half way point is
\r
9887 reached, then halves back down to the final destination,
\r
9888 which gives a nice slow in/out effect. The algorithmn
\r
9889 may seem to generate too many intermediates for short
\r
9890 moves, but remember that the purpose is to attract the
\r
9891 viewers attention to the piece about to be moved and
\r
9892 then to where it ends up. Too few frames would be less
\r
9896 Tween(start, mid, finish, factor, frames, nFrames)
\r
9897 POINT * start; POINT * mid;
\r
9898 POINT * finish; int factor;
\r
9899 POINT frames[]; int * nFrames;
\r
9901 int n, fraction = 1, count = 0;
\r
9903 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9904 for (n = 0; n < factor; n++)
\r
9906 for (n = 0; n < factor; n++) {
\r
9907 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9908 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9910 fraction = fraction / 2;
\r
9914 frames[count] = *mid;
\r
9917 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9919 for (n = 0; n < factor; n++) {
\r
9920 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9921 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9923 fraction = fraction * 2;
\r
9929 SettingsPopUp(ChessProgramState *cps)
\r
9930 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9931 EngineOptionsPopup(savedHwnd, cps);
\r
9934 int flock(int fid, int code)
\r
9936 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9940 ov.OffsetHigh = 0;
\r
9942 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9943 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9944 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9945 default: return -1;
\r