2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][41 ] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
257 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
259 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
262 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
265 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
266 { ABOUTBOX2, IDC_ChessBoard },
\r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
268 OPT_GameListClose, IDC_GameListDoFilter },
\r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
270 { DLG_Error, IDOK },
\r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
272 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
275 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
276 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
277 { DLG_IndexNumber, IDC_Index },
\r
278 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
279 { DLG_TypeInName, IDOK, IDCANCEL },
\r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
281 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
283 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
284 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
285 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
286 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
287 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
288 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
290 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
291 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
292 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
293 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
294 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
295 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
296 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
297 GPB_General, GPB_Alarm },
\r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
299 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
300 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
301 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
302 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
303 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
304 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
305 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont },
\r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
307 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
308 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
309 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
310 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
311 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
312 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
313 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
314 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
316 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
317 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
318 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
319 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
322 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
323 { DLG_MoveHistory },
\r
324 { DLG_EvalGraph },
\r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
328 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
329 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
330 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
332 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
333 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
337 static char languageBuf[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
501 int cliWidth, cliHeight;
\r
504 SizeInfo sizeInfo[] =
\r
506 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
507 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
508 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
509 { "petite", 33, 1, 1, 1, 0, 0 },
\r
510 { "slim", 37, 2, 1, 0, 0, 0 },
\r
511 { "small", 40, 2, 1, 0, 0, 0 },
\r
512 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
513 { "middling", 49, 2, 0, 0, 0, 0 },
\r
514 { "average", 54, 2, 0, 0, 0, 0 },
\r
515 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
516 { "medium", 64, 3, 0, 0, 0, 0 },
\r
517 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
518 { "large", 80, 3, 0, 0, 0, 0 },
\r
519 { "big", 87, 3, 0, 0, 0, 0 },
\r
520 { "huge", 95, 3, 0, 0, 0, 0 },
\r
521 { "giant", 108, 3, 0, 0, 0, 0 },
\r
522 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
523 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
524 { NULL, 0, 0, 0, 0, 0, 0 }
\r
527 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
528 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
539 { 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
540 { 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
541 { 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
542 { 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
543 { 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
544 { 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
545 { 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
546 { 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
547 { 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
550 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
559 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
560 #define N_BUTTONS 5
\r
562 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
564 {"<<", IDM_ToStart, NULL, NULL},
\r
565 {"<", IDM_Backward, NULL, NULL},
\r
566 {"P", IDM_Pause, NULL, NULL},
\r
567 {">", IDM_Forward, NULL, NULL},
\r
568 {">>", IDM_ToEnd, NULL, NULL},
\r
571 int tinyLayout = 0, smallLayout = 0;
\r
572 #define MENU_BAR_ITEMS 9
\r
573 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
574 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
575 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
579 MySound sounds[(int)NSoundClasses];
\r
580 MyTextAttribs textAttribs[(int)NColorClasses];
\r
582 MyColorizeAttribs colorizeAttribs[] = {
\r
583 { (COLORREF)0, 0, N_("Shout Text") },
\r
584 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
585 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
586 { (COLORREF)0, 0, N_("Channel Text") },
\r
587 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
588 { (COLORREF)0, 0, N_("Tell Text") },
\r
589 { (COLORREF)0, 0, N_("Challenge Text") },
\r
590 { (COLORREF)0, 0, N_("Request Text") },
\r
591 { (COLORREF)0, 0, N_("Seek Text") },
\r
592 { (COLORREF)0, 0, N_("Normal Text") },
\r
593 { (COLORREF)0, 0, N_("None") }
\r
598 static char *commentTitle;
\r
599 static char *commentText;
\r
600 static int commentIndex;
\r
601 static Boolean editComment = FALSE;
\r
604 char errorTitle[MSG_SIZ];
\r
605 char errorMessage[2*MSG_SIZ];
\r
606 HWND errorDialog = NULL;
\r
607 BOOLEAN moveErrorMessageUp = FALSE;
\r
608 BOOLEAN consoleEcho = TRUE;
\r
609 CHARFORMAT consoleCF;
\r
610 COLORREF consoleBackgroundColor;
\r
612 char *programVersion;
\r
618 typedef int CPKind;
\r
627 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
630 #define INPUT_SOURCE_BUF_SIZE 4096
\r
632 typedef struct _InputSource {
\r
639 char buf[INPUT_SOURCE_BUF_SIZE];
\r
643 InputCallback func;
\r
644 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
648 InputSource *consoleInputSource;
\r
653 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
654 VOID ConsoleCreate();
\r
656 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
657 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
658 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
659 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
661 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
662 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
663 void ParseIcsTextMenu(char *icsTextMenuString);
\r
664 VOID PopUpNameDialog(char firstchar);
\r
665 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
669 int GameListOptions();
\r
671 int dummy; // [HGM] for obsolete args
\r
673 HWND hwndMain = NULL; /* root window*/
\r
674 HWND hwndConsole = NULL;
\r
675 HWND commentDialog = NULL;
\r
676 HWND moveHistoryDialog = NULL;
\r
677 HWND evalGraphDialog = NULL;
\r
678 HWND engineOutputDialog = NULL;
\r
679 HWND gameListDialog = NULL;
\r
680 HWND editTagsDialog = NULL;
\r
682 int commentUp = FALSE;
\r
684 WindowPlacement wpMain;
\r
685 WindowPlacement wpConsole;
\r
686 WindowPlacement wpComment;
\r
687 WindowPlacement wpMoveHistory;
\r
688 WindowPlacement wpEvalGraph;
\r
689 WindowPlacement wpEngineOutput;
\r
690 WindowPlacement wpGameList;
\r
691 WindowPlacement wpTags;
\r
693 VOID EngineOptionsPopup(); // [HGM] settings
\r
695 VOID GothicPopUp(char *title, VariantClass variant);
\r
697 * Setting "frozen" should disable all user input other than deleting
\r
698 * the window. We do this while engines are initializing themselves.
\r
700 static int frozen = 0;
\r
701 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
707 if (frozen) return;
\r
709 hmenu = GetMenu(hwndMain);
\r
710 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
711 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
713 DrawMenuBar(hwndMain);
\r
716 /* Undo a FreezeUI */
\r
722 if (!frozen) return;
\r
724 hmenu = GetMenu(hwndMain);
\r
725 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
726 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
728 DrawMenuBar(hwndMain);
\r
731 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
733 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
739 #define JAWS_ALT_INTERCEPT
\r
740 #define JAWS_KBUP_NAVIGATION
\r
741 #define JAWS_KBDOWN_NAVIGATION
\r
742 #define JAWS_MENU_ITEMS
\r
743 #define JAWS_SILENCE
\r
744 #define JAWS_REPLAY
\r
746 #define JAWS_COPYRIGHT
\r
747 #define JAWS_DELETE(X) X
\r
748 #define SAYMACHINEMOVE()
\r
752 /*---------------------------------------------------------------------------*\
\r
756 \*---------------------------------------------------------------------------*/
\r
759 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
760 LPSTR lpCmdLine, int nCmdShow)
\r
763 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
764 // INITCOMMONCONTROLSEX ex;
\r
768 LoadLibrary("RICHED32.DLL");
\r
769 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
771 if (!InitApplication(hInstance)) {
\r
774 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
781 // InitCommonControlsEx(&ex);
\r
782 InitCommonControls();
\r
784 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
785 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
786 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
788 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
790 while (GetMessage(&msg, /* message structure */
\r
791 NULL, /* handle of window receiving the message */
\r
792 0, /* lowest message to examine */
\r
793 0)) /* highest message to examine */
\r
796 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
797 // [HGM] navigate: switch between all windows with tab
\r
798 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
799 int i, currentElement = 0;
\r
801 // first determine what element of the chain we come from (if any)
\r
802 if(appData.icsActive) {
\r
803 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
804 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
806 if(engineOutputDialog && EngineOutputIsUp()) {
\r
807 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
808 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
810 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
811 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
813 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
814 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
815 if(msg.hwnd == e1) currentElement = 2; else
\r
816 if(msg.hwnd == e2) currentElement = 3; else
\r
817 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
818 if(msg.hwnd == mh) currentElement = 4; else
\r
819 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
820 if(msg.hwnd == hText) currentElement = 5; else
\r
821 if(msg.hwnd == hInput) currentElement = 6; else
\r
822 for (i = 0; i < N_BUTTONS; i++) {
\r
823 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
826 // determine where to go to
\r
827 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
829 currentElement = (currentElement + direction) % 7;
\r
830 switch(currentElement) {
\r
832 h = hwndMain; break; // passing this case always makes the loop exit
\r
834 h = buttonDesc[0].hwnd; break; // could be NULL
\r
836 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
839 if(!EngineOutputIsUp()) continue;
\r
842 if(!MoveHistoryIsUp()) continue;
\r
844 // case 6: // input to eval graph does not seem to get here!
\r
845 // if(!EvalGraphIsUp()) continue;
\r
846 // h = evalGraphDialog; break;
\r
848 if(!appData.icsActive) continue;
\r
852 if(!appData.icsActive) continue;
\r
858 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
859 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
862 continue; // this message now has been processed
\r
866 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
867 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
868 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
869 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
870 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
871 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
872 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
873 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
874 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
875 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
876 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
877 for(i=0; i<MAX_CHAT; i++)
\r
878 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
881 if(done) continue; // [HGM] chat: end patch
\r
882 TranslateMessage(&msg); /* Translates virtual key codes */
\r
883 DispatchMessage(&msg); /* Dispatches message to window */
\r
888 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
891 /*---------------------------------------------------------------------------*\
\r
893 * Initialization functions
\r
895 \*---------------------------------------------------------------------------*/
\r
899 { // update user logo if necessary
\r
900 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
902 if(appData.autoLogo) {
\r
903 curName = UserName();
\r
904 if(strcmp(curName, oldUserName)) {
\r
905 GetCurrentDirectory(MSG_SIZ, dir);
\r
906 SetCurrentDirectory(installDir);
\r
907 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
908 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
909 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
910 if(userLogo == NULL)
\r
911 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
912 SetCurrentDirectory(dir); /* return to prev directory */
\r
918 InitApplication(HINSTANCE hInstance)
\r
922 /* Fill in window class structure with parameters that describe the */
\r
925 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
926 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
927 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
928 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
929 wc.hInstance = hInstance; /* Owner of this class */
\r
930 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
931 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
932 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
933 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
934 wc.lpszClassName = szAppName; /* Name to register as */
\r
936 /* Register the window class and return success/failure code. */
\r
937 if (!RegisterClass(&wc)) return FALSE;
\r
939 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
940 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
942 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
943 wc.hInstance = hInstance;
\r
944 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
945 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
946 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
947 wc.lpszMenuName = NULL;
\r
948 wc.lpszClassName = szConsoleName;
\r
950 if (!RegisterClass(&wc)) return FALSE;
\r
955 /* Set by InitInstance, used by EnsureOnScreen */
\r
956 int screenHeight, screenWidth;
\r
959 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
961 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
962 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
963 if (*x > screenWidth - 32) *x = 0;
\r
964 if (*y > screenHeight - 32) *y = 0;
\r
965 if (*x < minX) *x = minX;
\r
966 if (*y < minY) *y = minY;
\r
970 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
972 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
973 GetCurrentDirectory(MSG_SIZ, dir);
\r
974 SetCurrentDirectory(installDir);
\r
975 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
976 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
978 if (cps->programLogo == NULL && appData.debugMode) {
\r
979 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
981 } else if(appData.autoLogo) {
\r
982 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
983 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
984 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
986 if(appData.directory[n] && appData.directory[n][0]) {
\r
987 SetCurrentDirectory(appData.directory[n]);
\r
988 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
991 SetCurrentDirectory(dir); /* return to prev directory */
\r
997 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
998 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1000 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1001 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1002 liteBackTextureMode = appData.liteBackTextureMode;
\r
1004 if (liteBackTexture == NULL && appData.debugMode) {
\r
1005 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1009 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1010 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1011 darkBackTextureMode = appData.darkBackTextureMode;
\r
1013 if (darkBackTexture == NULL && appData.debugMode) {
\r
1014 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1020 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1022 HWND hwnd; /* Main window handle. */
\r
1024 WINDOWPLACEMENT wp;
\r
1027 hInst = hInstance; /* Store instance handle in our global variable */
\r
1028 programName = szAppName;
\r
1030 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1031 *filepart = NULLCHAR;
\r
1033 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1035 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1036 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1037 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1038 /* xboard, and older WinBoards, controlled the move sound with the
\r
1039 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1040 always turn the option on (so that the backend will call us),
\r
1041 then let the user turn the sound off by setting it to silence if
\r
1042 desired. To accommodate old winboard.ini files saved by old
\r
1043 versions of WinBoard, we also turn off the sound if the option
\r
1044 was initially set to false. [HGM] taken out of InitAppData */
\r
1045 if (!appData.ringBellAfterMoves) {
\r
1046 sounds[(int)SoundMove].name = strdup("");
\r
1047 appData.ringBellAfterMoves = TRUE;
\r
1049 if (appData.debugMode) {
\r
1050 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1051 setbuf(debugFP, NULL);
\r
1054 LoadLanguageFile(appData.language);
\r
1058 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1059 // InitEngineUCI( installDir, &second );
\r
1061 /* Create a main window for this application instance. */
\r
1062 hwnd = CreateWindow(szAppName, szTitle,
\r
1063 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1064 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1065 NULL, NULL, hInstance, NULL);
\r
1068 /* If window could not be created, return "failure" */
\r
1073 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1074 LoadLogo(&first, 0, FALSE);
\r
1075 LoadLogo(&second, 1, appData.icsActive);
\r
1079 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1080 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1081 iconCurrent = iconWhite;
\r
1082 InitDrawingColors();
\r
1083 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1084 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1085 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1086 /* Compute window size for each board size, and use the largest
\r
1087 size that fits on this screen as the default. */
\r
1088 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1089 if (boardSize == (BoardSize)-1 &&
\r
1090 winH <= screenHeight
\r
1091 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1092 && winW <= screenWidth) {
\r
1093 boardSize = (BoardSize)ibs;
\r
1097 InitDrawingSizes(boardSize, 0);
\r
1099 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1101 /* [AS] Load textures if specified */
\r
1104 mysrandom( (unsigned) time(NULL) );
\r
1106 /* [AS] Restore layout */
\r
1107 if( wpMoveHistory.visible ) {
\r
1108 MoveHistoryPopUp();
\r
1111 if( wpEvalGraph.visible ) {
\r
1115 if( wpEngineOutput.visible ) {
\r
1116 EngineOutputPopUp();
\r
1119 /* Make the window visible; update its client area; and return "success" */
\r
1120 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1121 wp.length = sizeof(WINDOWPLACEMENT);
\r
1123 wp.showCmd = nCmdShow;
\r
1124 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1125 wp.rcNormalPosition.left = wpMain.x;
\r
1126 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1127 wp.rcNormalPosition.top = wpMain.y;
\r
1128 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1129 SetWindowPlacement(hwndMain, &wp);
\r
1131 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1133 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1134 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1136 if (hwndConsole) {
\r
1138 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1139 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1141 ShowWindow(hwndConsole, nCmdShow);
\r
1142 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1143 char buf[MSG_SIZ], *p = buf, *q;
\r
1144 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1146 q = strchr(p, ';');
\r
1148 if(*p) ChatPopUp(p);
\r
1151 SetActiveWindow(hwndConsole);
\r
1153 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1154 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1163 HMENU hmenu = GetMenu(hwndMain);
\r
1165 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1166 MF_BYCOMMAND|((appData.icsActive &&
\r
1167 *appData.icsCommPort != NULLCHAR) ?
\r
1168 MF_ENABLED : MF_GRAYED));
\r
1169 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1170 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1171 MF_CHECKED : MF_UNCHECKED));
\r
1174 //---------------------------------------------------------------------------------------------------------
\r
1176 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1177 #define XBOARD FALSE
\r
1179 #define OPTCHAR "/"
\r
1180 #define SEPCHAR "="
\r
1184 // front-end part of option handling
\r
1187 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1189 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1190 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1193 lf->lfEscapement = 0;
\r
1194 lf->lfOrientation = 0;
\r
1195 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1196 lf->lfItalic = mfp->italic;
\r
1197 lf->lfUnderline = mfp->underline;
\r
1198 lf->lfStrikeOut = mfp->strikeout;
\r
1199 lf->lfCharSet = mfp->charset;
\r
1200 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1201 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1202 lf->lfQuality = DEFAULT_QUALITY;
\r
1203 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1204 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1208 CreateFontInMF(MyFont *mf)
\r
1210 LFfromMFP(&mf->lf, &mf->mfp);
\r
1211 if (mf->hf) DeleteObject(mf->hf);
\r
1212 mf->hf = CreateFontIndirect(&mf->lf);
\r
1215 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1217 colorVariable[] = {
\r
1218 &whitePieceColor,
\r
1219 &blackPieceColor,
\r
1220 &lightSquareColor,
\r
1221 &darkSquareColor,
\r
1222 &highlightSquareColor,
\r
1223 &premoveHighlightColor,
\r
1225 &consoleBackgroundColor,
\r
1226 &appData.fontForeColorWhite,
\r
1227 &appData.fontBackColorWhite,
\r
1228 &appData.fontForeColorBlack,
\r
1229 &appData.fontBackColorBlack,
\r
1230 &appData.evalHistColorWhite,
\r
1231 &appData.evalHistColorBlack,
\r
1232 &appData.highlightArrowColor,
\r
1235 /* Command line font name parser. NULL name means do nothing.
\r
1236 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1237 For backward compatibility, syntax without the colon is also
\r
1238 accepted, but font names with digits in them won't work in that case.
\r
1241 ParseFontName(char *name, MyFontParams *mfp)
\r
1244 if (name == NULL) return;
\r
1246 q = strchr(p, ':');
\r
1248 if (q - p >= sizeof(mfp->faceName))
\r
1249 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1250 memcpy(mfp->faceName, p, q - p);
\r
1251 mfp->faceName[q - p] = NULLCHAR;
\r
1254 q = mfp->faceName;
\r
1255 while (*p && !isdigit(*p)) {
\r
1257 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1258 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1260 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1263 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1264 mfp->pointSize = (float) atof(p);
\r
1265 mfp->bold = (strchr(p, 'b') != NULL);
\r
1266 mfp->italic = (strchr(p, 'i') != NULL);
\r
1267 mfp->underline = (strchr(p, 'u') != NULL);
\r
1268 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1269 mfp->charset = DEFAULT_CHARSET;
\r
1270 q = strchr(p, 'c');
\r
1272 mfp->charset = (BYTE) atoi(q+1);
\r
1276 ParseFont(char *name, int number)
\r
1277 { // wrapper to shield back-end from 'font'
\r
1278 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1283 { // in WB we have a 2D array of fonts; this initializes their description
\r
1285 /* Point font array elements to structures and
\r
1286 parse default font names */
\r
1287 for (i=0; i<NUM_FONTS; i++) {
\r
1288 for (j=0; j<NUM_SIZES; j++) {
\r
1289 font[j][i] = &fontRec[j][i];
\r
1290 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1297 { // here we create the actual fonts from the selected descriptions
\r
1299 for (i=0; i<NUM_FONTS; i++) {
\r
1300 for (j=0; j<NUM_SIZES; j++) {
\r
1301 CreateFontInMF(font[j][i]);
\r
1305 /* Color name parser.
\r
1306 X version accepts X color names, but this one
\r
1307 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1309 ParseColorName(char *name)
\r
1311 int red, green, blue, count;
\r
1312 char buf[MSG_SIZ];
\r
1314 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1316 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1317 &red, &green, &blue);
\r
1320 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1321 DisplayError(buf, 0);
\r
1322 return RGB(0, 0, 0);
\r
1324 return PALETTERGB(red, green, blue);
\r
1328 ParseColor(int n, char *name)
\r
1329 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1330 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1334 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1336 char *e = argValue;
\r
1340 if (*e == 'b') eff |= CFE_BOLD;
\r
1341 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1342 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1343 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1344 else if (*e == '#' || isdigit(*e)) break;
\r
1348 *color = ParseColorName(e);
\r
1352 ParseTextAttribs(ColorClass cc, char *s)
\r
1353 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1354 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1355 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1359 ParseBoardSize(void *addr, char *name)
\r
1360 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1361 BoardSize bs = SizeTiny;
\r
1362 while (sizeInfo[bs].name != NULL) {
\r
1363 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1364 *(BoardSize *)addr = bs;
\r
1369 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1374 { // [HGM] import name from appData first
\r
1377 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1378 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1379 textAttribs[cc].sound.data = NULL;
\r
1380 MyLoadSound(&textAttribs[cc].sound);
\r
1382 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1383 textAttribs[cc].sound.name = strdup("");
\r
1384 textAttribs[cc].sound.data = NULL;
\r
1386 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1387 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1388 sounds[sc].data = NULL;
\r
1389 MyLoadSound(&sounds[sc]);
\r
1394 SetCommPortDefaults()
\r
1396 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1397 dcb.DCBlength = sizeof(DCB);
\r
1398 dcb.BaudRate = 9600;
\r
1399 dcb.fBinary = TRUE;
\r
1400 dcb.fParity = FALSE;
\r
1401 dcb.fOutxCtsFlow = FALSE;
\r
1402 dcb.fOutxDsrFlow = FALSE;
\r
1403 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1404 dcb.fDsrSensitivity = FALSE;
\r
1405 dcb.fTXContinueOnXoff = TRUE;
\r
1406 dcb.fOutX = FALSE;
\r
1408 dcb.fNull = FALSE;
\r
1409 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1410 dcb.fAbortOnError = FALSE;
\r
1412 dcb.Parity = SPACEPARITY;
\r
1413 dcb.StopBits = ONESTOPBIT;
\r
1416 // [HGM] args: these three cases taken out to stay in front-end
\r
1418 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1419 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1420 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1421 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1423 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1424 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1425 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1426 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1427 ad->argName, mfp->faceName, mfp->pointSize,
\r
1428 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1429 mfp->bold ? "b" : "",
\r
1430 mfp->italic ? "i" : "",
\r
1431 mfp->underline ? "u" : "",
\r
1432 mfp->strikeout ? "s" : "",
\r
1433 (int)mfp->charset);
\r
1439 { // [HGM] copy the names from the internal WB variables to appData
\r
1442 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1443 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1444 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1445 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1449 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1450 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1451 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1452 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1453 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1454 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1455 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1456 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1457 (ta->effects) ? " " : "",
\r
1458 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1462 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1463 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1464 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1465 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1466 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1470 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1471 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1472 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1476 ParseCommPortSettings(char *s)
\r
1477 { // wrapper to keep dcb from back-end
\r
1478 ParseCommSettings(s, &dcb);
\r
1483 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1484 GetActualPlacement(hwndMain, &wpMain);
\r
1485 GetActualPlacement(hwndConsole, &wpConsole);
\r
1486 GetActualPlacement(commentDialog, &wpComment);
\r
1487 GetActualPlacement(editTagsDialog, &wpTags);
\r
1488 GetActualPlacement(gameListDialog, &wpGameList);
\r
1489 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1490 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1491 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1495 PrintCommPortSettings(FILE *f, char *name)
\r
1496 { // wrapper to shield back-end from DCB
\r
1497 PrintCommSettings(f, name, &dcb);
\r
1501 MySearchPath(char *installDir, char *name, char *fullname)
\r
1503 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1504 if(name[0]== '%') {
\r
1505 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1506 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1507 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1508 *strchr(buf, '%') = 0;
\r
1509 strcat(fullname, getenv(buf));
\r
1510 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1512 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1513 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1514 return (int) strlen(fullname);
\r
1516 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1520 MyGetFullPathName(char *name, char *fullname)
\r
1523 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1528 { // [HGM] args: allows testing if main window is realized from back-end
\r
1529 return hwndMain != NULL;
\r
1533 PopUpStartupDialog()
\r
1537 LoadLanguageFile(appData.language);
\r
1538 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1539 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1540 FreeProcInstance(lpProc);
\r
1543 /*---------------------------------------------------------------------------*\
\r
1545 * GDI board drawing routines
\r
1547 \*---------------------------------------------------------------------------*/
\r
1549 /* [AS] Draw square using background texture */
\r
1550 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1555 return; /* Should never happen! */
\r
1558 SetGraphicsMode( dst, GM_ADVANCED );
\r
1565 /* X reflection */
\r
1570 x.eDx = (FLOAT) dw + dx - 1;
\r
1573 SetWorldTransform( dst, &x );
\r
1576 /* Y reflection */
\r
1582 x.eDy = (FLOAT) dh + dy - 1;
\r
1584 SetWorldTransform( dst, &x );
\r
1592 x.eDx = (FLOAT) dx;
\r
1593 x.eDy = (FLOAT) dy;
\r
1596 SetWorldTransform( dst, &x );
\r
1600 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1608 SetWorldTransform( dst, &x );
\r
1610 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1613 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1615 PM_WP = (int) WhitePawn,
\r
1616 PM_WN = (int) WhiteKnight,
\r
1617 PM_WB = (int) WhiteBishop,
\r
1618 PM_WR = (int) WhiteRook,
\r
1619 PM_WQ = (int) WhiteQueen,
\r
1620 PM_WF = (int) WhiteFerz,
\r
1621 PM_WW = (int) WhiteWazir,
\r
1622 PM_WE = (int) WhiteAlfil,
\r
1623 PM_WM = (int) WhiteMan,
\r
1624 PM_WO = (int) WhiteCannon,
\r
1625 PM_WU = (int) WhiteUnicorn,
\r
1626 PM_WH = (int) WhiteNightrider,
\r
1627 PM_WA = (int) WhiteAngel,
\r
1628 PM_WC = (int) WhiteMarshall,
\r
1629 PM_WAB = (int) WhiteCardinal,
\r
1630 PM_WD = (int) WhiteDragon,
\r
1631 PM_WL = (int) WhiteLance,
\r
1632 PM_WS = (int) WhiteCobra,
\r
1633 PM_WV = (int) WhiteFalcon,
\r
1634 PM_WSG = (int) WhiteSilver,
\r
1635 PM_WG = (int) WhiteGrasshopper,
\r
1636 PM_WK = (int) WhiteKing,
\r
1637 PM_BP = (int) BlackPawn,
\r
1638 PM_BN = (int) BlackKnight,
\r
1639 PM_BB = (int) BlackBishop,
\r
1640 PM_BR = (int) BlackRook,
\r
1641 PM_BQ = (int) BlackQueen,
\r
1642 PM_BF = (int) BlackFerz,
\r
1643 PM_BW = (int) BlackWazir,
\r
1644 PM_BE = (int) BlackAlfil,
\r
1645 PM_BM = (int) BlackMan,
\r
1646 PM_BO = (int) BlackCannon,
\r
1647 PM_BU = (int) BlackUnicorn,
\r
1648 PM_BH = (int) BlackNightrider,
\r
1649 PM_BA = (int) BlackAngel,
\r
1650 PM_BC = (int) BlackMarshall,
\r
1651 PM_BG = (int) BlackGrasshopper,
\r
1652 PM_BAB = (int) BlackCardinal,
\r
1653 PM_BD = (int) BlackDragon,
\r
1654 PM_BL = (int) BlackLance,
\r
1655 PM_BS = (int) BlackCobra,
\r
1656 PM_BV = (int) BlackFalcon,
\r
1657 PM_BSG = (int) BlackSilver,
\r
1658 PM_BK = (int) BlackKing
\r
1661 static HFONT hPieceFont = NULL;
\r
1662 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1663 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1664 static int fontBitmapSquareSize = 0;
\r
1665 static char pieceToFontChar[(int) EmptySquare] =
\r
1666 { 'p', 'n', 'b', 'r', 'q',
\r
1667 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1668 'k', 'o', 'm', 'v', 't', 'w',
\r
1669 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1672 extern BOOL SetCharTable( char *table, const char * map );
\r
1673 /* [HGM] moved to backend.c */
\r
1675 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1678 BYTE r1 = GetRValue( color );
\r
1679 BYTE g1 = GetGValue( color );
\r
1680 BYTE b1 = GetBValue( color );
\r
1686 /* Create a uniform background first */
\r
1687 hbrush = CreateSolidBrush( color );
\r
1688 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1689 FillRect( hdc, &rc, hbrush );
\r
1690 DeleteObject( hbrush );
\r
1693 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1694 int steps = squareSize / 2;
\r
1697 for( i=0; i<steps; i++ ) {
\r
1698 BYTE r = r1 - (r1-r2) * i / steps;
\r
1699 BYTE g = g1 - (g1-g2) * i / steps;
\r
1700 BYTE b = b1 - (b1-b2) * i / steps;
\r
1702 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1703 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1704 FillRect( hdc, &rc, hbrush );
\r
1705 DeleteObject(hbrush);
\r
1708 else if( mode == 2 ) {
\r
1709 /* Diagonal gradient, good more or less for every piece */
\r
1710 POINT triangle[3];
\r
1711 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1712 HBRUSH hbrush_old;
\r
1713 int steps = squareSize;
\r
1716 triangle[0].x = squareSize - steps;
\r
1717 triangle[0].y = squareSize;
\r
1718 triangle[1].x = squareSize;
\r
1719 triangle[1].y = squareSize;
\r
1720 triangle[2].x = squareSize;
\r
1721 triangle[2].y = squareSize - steps;
\r
1723 for( i=0; i<steps; i++ ) {
\r
1724 BYTE r = r1 - (r1-r2) * i / steps;
\r
1725 BYTE g = g1 - (g1-g2) * i / steps;
\r
1726 BYTE b = b1 - (b1-b2) * i / steps;
\r
1728 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1729 hbrush_old = SelectObject( hdc, hbrush );
\r
1730 Polygon( hdc, triangle, 3 );
\r
1731 SelectObject( hdc, hbrush_old );
\r
1732 DeleteObject(hbrush);
\r
1737 SelectObject( hdc, hpen );
\r
1742 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1743 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1744 piece: follow the steps as explained below.
\r
1746 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1750 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1754 int backColor = whitePieceColor;
\r
1755 int foreColor = blackPieceColor;
\r
1757 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1758 backColor = appData.fontBackColorWhite;
\r
1759 foreColor = appData.fontForeColorWhite;
\r
1761 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1762 backColor = appData.fontBackColorBlack;
\r
1763 foreColor = appData.fontForeColorBlack;
\r
1767 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1769 hbm_old = SelectObject( hdc, hbm );
\r
1773 rc.right = squareSize;
\r
1774 rc.bottom = squareSize;
\r
1776 /* Step 1: background is now black */
\r
1777 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1779 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1781 pt.x = (squareSize - sz.cx) / 2;
\r
1782 pt.y = (squareSize - sz.cy) / 2;
\r
1784 SetBkMode( hdc, TRANSPARENT );
\r
1785 SetTextColor( hdc, chroma );
\r
1786 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1787 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1789 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1790 /* Step 3: the area outside the piece is filled with white */
\r
1791 // FloodFill( hdc, 0, 0, chroma );
\r
1792 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1793 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1794 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1795 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1796 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1798 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1799 but if the start point is not inside the piece we're lost!
\r
1800 There should be a better way to do this... if we could create a region or path
\r
1801 from the fill operation we would be fine for example.
\r
1803 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1804 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1806 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1807 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1808 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1810 SelectObject( dc2, bm2 );
\r
1811 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1812 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1813 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1814 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1815 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1818 DeleteObject( bm2 );
\r
1821 SetTextColor( hdc, 0 );
\r
1823 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1824 draw the piece again in black for safety.
\r
1826 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1828 SelectObject( hdc, hbm_old );
\r
1830 if( hPieceMask[index] != NULL ) {
\r
1831 DeleteObject( hPieceMask[index] );
\r
1834 hPieceMask[index] = hbm;
\r
1837 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1839 SelectObject( hdc, hbm );
\r
1842 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1843 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1844 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1846 SelectObject( dc1, hPieceMask[index] );
\r
1847 SelectObject( dc2, bm2 );
\r
1848 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1849 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1852 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1853 the piece background and deletes (makes transparent) the rest.
\r
1854 Thanks to that mask, we are free to paint the background with the greates
\r
1855 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1856 We use this, to make gradients and give the pieces a "roundish" look.
\r
1858 SetPieceBackground( hdc, backColor, 2 );
\r
1859 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1863 DeleteObject( bm2 );
\r
1866 SetTextColor( hdc, foreColor );
\r
1867 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1869 SelectObject( hdc, hbm_old );
\r
1871 if( hPieceFace[index] != NULL ) {
\r
1872 DeleteObject( hPieceFace[index] );
\r
1875 hPieceFace[index] = hbm;
\r
1878 static int TranslatePieceToFontPiece( int piece )
\r
1908 case BlackMarshall:
\r
1912 case BlackNightrider:
\r
1918 case BlackUnicorn:
\r
1922 case BlackGrasshopper:
\r
1934 case BlackCardinal:
\r
1941 case WhiteMarshall:
\r
1945 case WhiteNightrider:
\r
1951 case WhiteUnicorn:
\r
1955 case WhiteGrasshopper:
\r
1967 case WhiteCardinal:
\r
1976 void CreatePiecesFromFont()
\r
1979 HDC hdc_window = NULL;
\r
1985 if( fontBitmapSquareSize < 0 ) {
\r
1986 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1990 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1991 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1992 fontBitmapSquareSize = -1;
\r
1996 if( fontBitmapSquareSize != squareSize ) {
\r
1997 hdc_window = GetDC( hwndMain );
\r
1998 hdc = CreateCompatibleDC( hdc_window );
\r
2000 if( hPieceFont != NULL ) {
\r
2001 DeleteObject( hPieceFont );
\r
2004 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2005 hPieceMask[i] = NULL;
\r
2006 hPieceFace[i] = NULL;
\r
2012 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2013 fontHeight = appData.fontPieceSize;
\r
2016 fontHeight = (fontHeight * squareSize) / 100;
\r
2018 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2020 lf.lfEscapement = 0;
\r
2021 lf.lfOrientation = 0;
\r
2022 lf.lfWeight = FW_NORMAL;
\r
2024 lf.lfUnderline = 0;
\r
2025 lf.lfStrikeOut = 0;
\r
2026 lf.lfCharSet = DEFAULT_CHARSET;
\r
2027 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2028 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2029 lf.lfQuality = PROOF_QUALITY;
\r
2030 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2031 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2032 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2034 hPieceFont = CreateFontIndirect( &lf );
\r
2036 if( hPieceFont == NULL ) {
\r
2037 fontBitmapSquareSize = -2;
\r
2040 /* Setup font-to-piece character table */
\r
2041 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2042 /* No (or wrong) global settings, try to detect the font */
\r
2043 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2045 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2047 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2048 /* DiagramTT* family */
\r
2049 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2051 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2052 /* Fairy symbols */
\r
2053 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2055 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2056 /* Good Companion (Some characters get warped as literal :-( */
\r
2057 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2058 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2059 SetCharTable(pieceToFontChar, s);
\r
2062 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2063 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2067 /* Create bitmaps */
\r
2068 hfont_old = SelectObject( hdc, hPieceFont );
\r
2069 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2070 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2071 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2073 SelectObject( hdc, hfont_old );
\r
2075 fontBitmapSquareSize = squareSize;
\r
2079 if( hdc != NULL ) {
\r
2083 if( hdc_window != NULL ) {
\r
2084 ReleaseDC( hwndMain, hdc_window );
\r
2089 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2093 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2094 if (gameInfo.event &&
\r
2095 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2096 strcmp(name, "k80s") == 0) {
\r
2097 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2099 return LoadBitmap(hinst, name);
\r
2103 /* Insert a color into the program's logical palette
\r
2104 structure. This code assumes the given color is
\r
2105 the result of the RGB or PALETTERGB macro, and it
\r
2106 knows how those macros work (which is documented).
\r
2109 InsertInPalette(COLORREF color)
\r
2111 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2113 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2114 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2115 pLogPal->palNumEntries--;
\r
2119 pe->peFlags = (char) 0;
\r
2120 pe->peRed = (char) (0xFF & color);
\r
2121 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2122 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2128 InitDrawingColors()
\r
2130 if (pLogPal == NULL) {
\r
2131 /* Allocate enough memory for a logical palette with
\r
2132 * PALETTESIZE entries and set the size and version fields
\r
2133 * of the logical palette structure.
\r
2135 pLogPal = (NPLOGPALETTE)
\r
2136 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2137 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2138 pLogPal->palVersion = 0x300;
\r
2140 pLogPal->palNumEntries = 0;
\r
2142 InsertInPalette(lightSquareColor);
\r
2143 InsertInPalette(darkSquareColor);
\r
2144 InsertInPalette(whitePieceColor);
\r
2145 InsertInPalette(blackPieceColor);
\r
2146 InsertInPalette(highlightSquareColor);
\r
2147 InsertInPalette(premoveHighlightColor);
\r
2149 /* create a logical color palette according the information
\r
2150 * in the LOGPALETTE structure.
\r
2152 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2154 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2155 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2156 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2157 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2158 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2159 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2160 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2161 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2162 /* [AS] Force rendering of the font-based pieces */
\r
2163 if( fontBitmapSquareSize > 0 ) {
\r
2164 fontBitmapSquareSize = 0;
\r
2170 BoardWidth(int boardSize, int n)
\r
2171 { /* [HGM] argument n added to allow different width and height */
\r
2172 int lineGap = sizeInfo[boardSize].lineGap;
\r
2174 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2175 lineGap = appData.overrideLineGap;
\r
2178 return (n + 1) * lineGap +
\r
2179 n * sizeInfo[boardSize].squareSize;
\r
2182 /* Respond to board resize by dragging edge */
\r
2184 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2186 BoardSize newSize = NUM_SIZES - 1;
\r
2187 static int recurse = 0;
\r
2188 if (IsIconic(hwndMain)) return;
\r
2189 if (recurse > 0) return;
\r
2191 while (newSize > 0) {
\r
2192 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2193 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2194 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2197 boardSize = newSize;
\r
2198 InitDrawingSizes(boardSize, flags);
\r
2203 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2206 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2208 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2209 ChessSquare piece;
\r
2210 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2212 SIZE clockSize, messageSize;
\r
2214 char buf[MSG_SIZ];
\r
2216 HMENU hmenu = GetMenu(hwndMain);
\r
2217 RECT crect, wrect, oldRect;
\r
2219 LOGBRUSH logbrush;
\r
2221 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2222 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2224 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2225 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2227 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2228 oldRect.top = wpMain.y;
\r
2229 oldRect.right = wpMain.x + wpMain.width;
\r
2230 oldRect.bottom = wpMain.y + wpMain.height;
\r
2232 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2233 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2234 squareSize = sizeInfo[boardSize].squareSize;
\r
2235 lineGap = sizeInfo[boardSize].lineGap;
\r
2236 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2238 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2239 lineGap = appData.overrideLineGap;
\r
2242 if (tinyLayout != oldTinyLayout) {
\r
2243 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2245 style &= ~WS_SYSMENU;
\r
2246 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2247 "&Minimize\tCtrl+F4");
\r
2249 style |= WS_SYSMENU;
\r
2250 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2252 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2254 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2255 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2256 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2258 DrawMenuBar(hwndMain);
\r
2261 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2262 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2264 /* Get text area sizes */
\r
2265 hdc = GetDC(hwndMain);
\r
2266 if (appData.clockMode) {
\r
2267 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2269 snprintf(buf, MSG_SIZ, _("White"));
\r
2271 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2272 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2273 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2274 str = _("We only care about the height here");
\r
2275 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2276 SelectObject(hdc, oldFont);
\r
2277 ReleaseDC(hwndMain, hdc);
\r
2279 /* Compute where everything goes */
\r
2280 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2281 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2282 logoHeight = 2*clockSize.cy;
\r
2283 leftLogoRect.left = OUTER_MARGIN;
\r
2284 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2285 leftLogoRect.top = OUTER_MARGIN;
\r
2286 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2288 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2289 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2290 rightLogoRect.top = OUTER_MARGIN;
\r
2291 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2294 whiteRect.left = leftLogoRect.right;
\r
2295 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2296 whiteRect.top = OUTER_MARGIN;
\r
2297 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2299 blackRect.right = rightLogoRect.left;
\r
2300 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2301 blackRect.top = whiteRect.top;
\r
2302 blackRect.bottom = whiteRect.bottom;
\r
2304 whiteRect.left = OUTER_MARGIN;
\r
2305 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2306 whiteRect.top = OUTER_MARGIN;
\r
2307 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2309 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2310 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2311 blackRect.top = whiteRect.top;
\r
2312 blackRect.bottom = whiteRect.bottom;
\r
2314 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2317 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2318 if (appData.showButtonBar) {
\r
2319 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2320 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2322 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2324 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2325 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2327 boardRect.left = OUTER_MARGIN;
\r
2328 boardRect.right = boardRect.left + boardWidth;
\r
2329 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2330 boardRect.bottom = boardRect.top + boardHeight;
\r
2332 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2333 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2334 oldBoardSize = boardSize;
\r
2335 oldTinyLayout = tinyLayout;
\r
2336 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2337 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2338 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2339 winW *= 1 + twoBoards;
\r
2340 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2341 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2342 wpMain.height = winH; // without disturbing window attachments
\r
2343 GetWindowRect(hwndMain, &wrect);
\r
2344 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2345 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2347 // [HGM] placement: let attached windows follow size change.
\r
2348 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2349 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2350 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2351 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2352 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2354 /* compensate if menu bar wrapped */
\r
2355 GetClientRect(hwndMain, &crect);
\r
2356 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2357 wpMain.height += offby;
\r
2359 case WMSZ_TOPLEFT:
\r
2360 SetWindowPos(hwndMain, NULL,
\r
2361 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2362 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2365 case WMSZ_TOPRIGHT:
\r
2367 SetWindowPos(hwndMain, NULL,
\r
2368 wrect.left, wrect.bottom - wpMain.height,
\r
2369 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2372 case WMSZ_BOTTOMLEFT:
\r
2374 SetWindowPos(hwndMain, NULL,
\r
2375 wrect.right - wpMain.width, wrect.top,
\r
2376 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2379 case WMSZ_BOTTOMRIGHT:
\r
2383 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2384 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2389 for (i = 0; i < N_BUTTONS; i++) {
\r
2390 if (buttonDesc[i].hwnd != NULL) {
\r
2391 DestroyWindow(buttonDesc[i].hwnd);
\r
2392 buttonDesc[i].hwnd = NULL;
\r
2394 if (appData.showButtonBar) {
\r
2395 buttonDesc[i].hwnd =
\r
2396 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2397 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2398 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2399 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2400 (HMENU) buttonDesc[i].id,
\r
2401 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2403 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2404 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2405 MAKELPARAM(FALSE, 0));
\r
2407 if (buttonDesc[i].id == IDM_Pause)
\r
2408 hwndPause = buttonDesc[i].hwnd;
\r
2409 buttonDesc[i].wndproc = (WNDPROC)
\r
2410 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2413 if (gridPen != NULL) DeleteObject(gridPen);
\r
2414 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2415 if (premovePen != NULL) DeleteObject(premovePen);
\r
2416 if (lineGap != 0) {
\r
2417 logbrush.lbStyle = BS_SOLID;
\r
2418 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2420 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2421 lineGap, &logbrush, 0, NULL);
\r
2422 logbrush.lbColor = highlightSquareColor;
\r
2424 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2425 lineGap, &logbrush, 0, NULL);
\r
2427 logbrush.lbColor = premoveHighlightColor;
\r
2429 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2430 lineGap, &logbrush, 0, NULL);
\r
2432 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2433 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2434 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2435 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2436 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2437 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2438 BOARD_WIDTH * (squareSize + lineGap);
\r
2439 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2441 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2442 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2443 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2444 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2445 lineGap / 2 + (i * (squareSize + lineGap));
\r
2446 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2447 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2448 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2452 /* [HGM] Licensing requirement */
\r
2454 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2457 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2459 GothicPopUp( "", VariantNormal);
\r
2462 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2464 /* Load piece bitmaps for this board size */
\r
2465 for (i=0; i<=2; i++) {
\r
2466 for (piece = WhitePawn;
\r
2467 (int) piece < (int) BlackPawn;
\r
2468 piece = (ChessSquare) ((int) piece + 1)) {
\r
2469 if (pieceBitmap[i][piece] != NULL)
\r
2470 DeleteObject(pieceBitmap[i][piece]);
\r
2474 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2475 // Orthodox Chess pieces
\r
2476 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2477 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2478 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2479 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2480 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2481 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2482 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2483 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2484 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2485 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2486 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2487 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2488 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2489 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2490 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2491 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2492 // in Shogi, Hijack the unused Queen for Lance
\r
2493 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2497 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2498 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2499 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2502 if(squareSize <= 72 && squareSize >= 33) {
\r
2503 /* A & C are available in most sizes now */
\r
2504 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2505 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2511 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2512 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2513 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2517 } else { // Smirf-like
\r
2518 if(gameInfo.variant == VariantSChess) {
\r
2519 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2520 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2521 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2523 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2528 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2529 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2530 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2531 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2532 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2533 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2536 } else { // WinBoard standard
\r
2537 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2538 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2539 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2544 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2545 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2569 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2572 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2573 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2574 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2576 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2577 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2583 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2586 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2593 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2596 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2599 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2600 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2601 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2604 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2605 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2606 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2607 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2608 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2609 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2610 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2611 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2612 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2613 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2614 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2615 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2616 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2617 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2618 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2622 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2623 /* special Shogi support in this size */
\r
2624 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2625 for (piece = WhitePawn;
\r
2626 (int) piece < (int) BlackPawn;
\r
2627 piece = (ChessSquare) ((int) piece + 1)) {
\r
2628 if (pieceBitmap[i][piece] != NULL)
\r
2629 DeleteObject(pieceBitmap[i][piece]);
\r
2632 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2639 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2640 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2641 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2642 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2643 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2644 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2645 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2646 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2653 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2654 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2655 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2656 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2657 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2658 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2659 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2660 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2667 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2668 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2669 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2670 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2671 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2672 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2673 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2679 PieceBitmap(ChessSquare p, int kind)
\r
2681 if ((int) p >= (int) BlackPawn)
\r
2682 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2684 return pieceBitmap[kind][(int) p];
\r
2687 /***************************************************************/
\r
2689 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2690 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2692 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2693 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2697 SquareToPos(int row, int column, int * x, int * y)
\r
2700 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2701 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2703 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2704 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2709 DrawCoordsOnDC(HDC hdc)
\r
2711 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2712 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2713 char str[2] = { NULLCHAR, NULLCHAR };
\r
2714 int oldMode, oldAlign, x, y, start, i;
\r
2718 if (!appData.showCoords)
\r
2721 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2723 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2724 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2725 oldAlign = GetTextAlign(hdc);
\r
2726 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2728 y = boardRect.top + lineGap;
\r
2729 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2731 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2732 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2733 str[0] = files[start + i];
\r
2734 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2735 y += squareSize + lineGap;
\r
2738 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2740 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2741 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2742 str[0] = ranks[start + i];
\r
2743 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2744 x += squareSize + lineGap;
\r
2747 SelectObject(hdc, oldBrush);
\r
2748 SetBkMode(hdc, oldMode);
\r
2749 SetTextAlign(hdc, oldAlign);
\r
2750 SelectObject(hdc, oldFont);
\r
2754 DrawGridOnDC(HDC hdc)
\r
2758 if (lineGap != 0) {
\r
2759 oldPen = SelectObject(hdc, gridPen);
\r
2760 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2761 SelectObject(hdc, oldPen);
\r
2765 #define HIGHLIGHT_PEN 0
\r
2766 #define PREMOVE_PEN 1
\r
2769 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2772 HPEN oldPen, hPen;
\r
2773 if (lineGap == 0) return;
\r
2775 x1 = boardRect.left +
\r
2776 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2777 y1 = boardRect.top +
\r
2778 lineGap/2 + y * (squareSize + lineGap);
\r
2780 x1 = boardRect.left +
\r
2781 lineGap/2 + x * (squareSize + lineGap);
\r
2782 y1 = boardRect.top +
\r
2783 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2785 hPen = pen ? premovePen : highlightPen;
\r
2786 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2787 MoveToEx(hdc, x1, y1, NULL);
\r
2788 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2789 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2790 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2791 LineTo(hdc, x1, y1);
\r
2792 SelectObject(hdc, oldPen);
\r
2796 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2799 for (i=0; i<2; i++) {
\r
2800 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2801 DrawHighlightOnDC(hdc, TRUE,
\r
2802 h->sq[i].x, h->sq[i].y,
\r
2807 /* Note: sqcolor is used only in monoMode */
\r
2808 /* Note that this code is largely duplicated in woptions.c,
\r
2809 function DrawSampleSquare, so that needs to be updated too */
\r
2811 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2813 HBITMAP oldBitmap;
\r
2817 if (appData.blindfold) return;
\r
2819 /* [AS] Use font-based pieces if needed */
\r
2820 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2821 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2822 CreatePiecesFromFont();
\r
2824 if( fontBitmapSquareSize == squareSize ) {
\r
2825 int index = TranslatePieceToFontPiece(piece);
\r
2827 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2829 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2830 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2834 squareSize, squareSize,
\r
2839 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2841 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2842 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2846 squareSize, squareSize,
\r
2855 if (appData.monoMode) {
\r
2856 SelectObject(tmphdc, PieceBitmap(piece,
\r
2857 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2858 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2859 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2861 tmpSize = squareSize;
\r
2863 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2864 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2865 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2866 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2867 x += (squareSize - minorSize)>>1;
\r
2868 y += squareSize - minorSize - 2;
\r
2869 tmpSize = minorSize;
\r
2871 if (color || appData.allWhite ) {
\r
2872 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2874 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2875 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2876 if(appData.upsideDown && color==flipView)
\r
2877 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2879 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2880 /* Use black for outline of white pieces */
\r
2881 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2882 if(appData.upsideDown && color==flipView)
\r
2883 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2885 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2887 /* Use square color for details of black pieces */
\r
2888 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2889 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2890 if(appData.upsideDown && !flipView)
\r
2891 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2893 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2895 SelectObject(hdc, oldBrush);
\r
2896 SelectObject(tmphdc, oldBitmap);
\r
2900 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2901 int GetBackTextureMode( int algo )
\r
2903 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2907 case BACK_TEXTURE_MODE_PLAIN:
\r
2908 result = 1; /* Always use identity map */
\r
2910 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2911 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2919 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2920 to handle redraws cleanly (as random numbers would always be different).
\r
2922 VOID RebuildTextureSquareInfo()
\r
2932 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2934 if( liteBackTexture != NULL ) {
\r
2935 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2936 lite_w = bi.bmWidth;
\r
2937 lite_h = bi.bmHeight;
\r
2941 if( darkBackTexture != NULL ) {
\r
2942 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2943 dark_w = bi.bmWidth;
\r
2944 dark_h = bi.bmHeight;
\r
2948 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2949 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2950 if( (col + row) & 1 ) {
\r
2952 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2953 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2954 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2956 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2957 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2958 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2960 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2961 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2966 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2967 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2968 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2970 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2971 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2972 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2974 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2975 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2982 /* [AS] Arrow highlighting support */
\r
2984 static double A_WIDTH = 5; /* Width of arrow body */
\r
2986 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2987 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2989 static double Sqr( double x )
\r
2994 static int Round( double x )
\r
2996 return (int) (x + 0.5);
\r
2999 /* Draw an arrow between two points using current settings */
\r
3000 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3003 double dx, dy, j, k, x, y;
\r
3005 if( d_x == s_x ) {
\r
3006 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3008 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3011 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3012 arrow[1].y = d_y - h;
\r
3014 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3015 arrow[2].y = d_y - h;
\r
3020 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3021 arrow[5].y = d_y - h;
\r
3023 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3024 arrow[4].y = d_y - h;
\r
3026 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3029 else if( d_y == s_y ) {
\r
3030 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3033 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3035 arrow[1].x = d_x - w;
\r
3036 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3038 arrow[2].x = d_x - w;
\r
3039 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3044 arrow[5].x = d_x - w;
\r
3045 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3047 arrow[4].x = d_x - w;
\r
3048 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3051 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3054 /* [AS] Needed a lot of paper for this! :-) */
\r
3055 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3056 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3058 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3060 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3065 arrow[0].x = Round(x - j);
\r
3066 arrow[0].y = Round(y + j*dx);
\r
3068 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3069 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3072 x = (double) d_x - k;
\r
3073 y = (double) d_y - k*dy;
\r
3076 x = (double) d_x + k;
\r
3077 y = (double) d_y + k*dy;
\r
3080 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3082 arrow[6].x = Round(x - j);
\r
3083 arrow[6].y = Round(y + j*dx);
\r
3085 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3086 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3088 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3089 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3094 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3095 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3098 Polygon( hdc, arrow, 7 );
\r
3101 /* [AS] Draw an arrow between two squares */
\r
3102 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3104 int s_x, s_y, d_x, d_y;
\r
3111 if( s_col == d_col && s_row == d_row ) {
\r
3115 /* Get source and destination points */
\r
3116 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3117 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3120 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3122 else if( d_y < s_y ) {
\r
3123 d_y += squareSize / 2 + squareSize / 4;
\r
3126 d_y += squareSize / 2;
\r
3130 d_x += squareSize / 2 - squareSize / 4;
\r
3132 else if( d_x < s_x ) {
\r
3133 d_x += squareSize / 2 + squareSize / 4;
\r
3136 d_x += squareSize / 2;
\r
3139 s_x += squareSize / 2;
\r
3140 s_y += squareSize / 2;
\r
3142 /* Adjust width */
\r
3143 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3146 stLB.lbStyle = BS_SOLID;
\r
3147 stLB.lbColor = appData.highlightArrowColor;
\r
3150 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3151 holdpen = SelectObject( hdc, hpen );
\r
3152 hbrush = CreateBrushIndirect( &stLB );
\r
3153 holdbrush = SelectObject( hdc, hbrush );
\r
3155 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3157 SelectObject( hdc, holdpen );
\r
3158 SelectObject( hdc, holdbrush );
\r
3159 DeleteObject( hpen );
\r
3160 DeleteObject( hbrush );
\r
3163 BOOL HasHighlightInfo()
\r
3165 BOOL result = FALSE;
\r
3167 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3168 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3176 BOOL IsDrawArrowEnabled()
\r
3178 BOOL result = FALSE;
\r
3180 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3187 VOID DrawArrowHighlight( HDC hdc )
\r
3189 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3190 DrawArrowBetweenSquares( hdc,
\r
3191 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3192 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3196 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3198 HRGN result = NULL;
\r
3200 if( HasHighlightInfo() ) {
\r
3201 int x1, y1, x2, y2;
\r
3202 int sx, sy, dx, dy;
\r
3204 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3205 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3207 sx = MIN( x1, x2 );
\r
3208 sy = MIN( y1, y2 );
\r
3209 dx = MAX( x1, x2 ) + squareSize;
\r
3210 dy = MAX( y1, y2 ) + squareSize;
\r
3212 result = CreateRectRgn( sx, sy, dx, dy );
\r
3219 Warning: this function modifies the behavior of several other functions.
\r
3221 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3222 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3223 repaint is scattered all over the place, which is not good for features such as
\r
3224 "arrow highlighting" that require a full repaint of the board.
\r
3226 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3227 user interaction, when speed is not so important) but especially to avoid errors
\r
3228 in the displayed graphics.
\r
3230 In such patched places, I always try refer to this function so there is a single
\r
3231 place to maintain knowledge.
\r
3233 To restore the original behavior, just return FALSE unconditionally.
\r
3235 BOOL IsFullRepaintPreferrable()
\r
3237 BOOL result = FALSE;
\r
3239 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3240 /* Arrow may appear on the board */
\r
3248 This function is called by DrawPosition to know whether a full repaint must
\r
3251 Only DrawPosition may directly call this function, which makes use of
\r
3252 some state information. Other function should call DrawPosition specifying
\r
3253 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3255 BOOL DrawPositionNeedsFullRepaint()
\r
3257 BOOL result = FALSE;
\r
3260 Probably a slightly better policy would be to trigger a full repaint
\r
3261 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3262 but animation is fast enough that it's difficult to notice.
\r
3264 if( animInfo.piece == EmptySquare ) {
\r
3265 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3274 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3276 int row, column, x, y, square_color, piece_color;
\r
3277 ChessSquare piece;
\r
3279 HDC texture_hdc = NULL;
\r
3281 /* [AS] Initialize background textures if needed */
\r
3282 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3283 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3284 if( backTextureSquareSize != squareSize
\r
3285 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3286 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3287 backTextureSquareSize = squareSize;
\r
3288 RebuildTextureSquareInfo();
\r
3291 texture_hdc = CreateCompatibleDC( hdc );
\r
3294 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3295 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3297 SquareToPos(row, column, &x, &y);
\r
3299 piece = board[row][column];
\r
3301 square_color = ((column + row) % 2) == 1;
\r
3302 if( gameInfo.variant == VariantXiangqi ) {
\r
3303 square_color = !InPalace(row, column);
\r
3304 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3305 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3307 piece_color = (int) piece < (int) BlackPawn;
\r
3310 /* [HGM] holdings file: light square or black */
\r
3311 if(column == BOARD_LEFT-2) {
\r
3312 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3315 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3319 if(column == BOARD_RGHT + 1 ) {
\r
3320 if( row < gameInfo.holdingsSize )
\r
3323 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3327 if(column == BOARD_LEFT-1 ) /* left align */
\r
3328 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3329 else if( column == BOARD_RGHT) /* right align */
\r
3330 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3332 if (appData.monoMode) {
\r
3333 if (piece == EmptySquare) {
\r
3334 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3335 square_color ? WHITENESS : BLACKNESS);
\r
3337 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3340 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3341 /* [AS] Draw the square using a texture bitmap */
\r
3342 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3343 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3344 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3347 squareSize, squareSize,
\r
3350 backTextureSquareInfo[r][c].mode,
\r
3351 backTextureSquareInfo[r][c].x,
\r
3352 backTextureSquareInfo[r][c].y );
\r
3354 SelectObject( texture_hdc, hbm );
\r
3356 if (piece != EmptySquare) {
\r
3357 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3361 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3363 oldBrush = SelectObject(hdc, brush );
\r
3364 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3365 SelectObject(hdc, oldBrush);
\r
3366 if (piece != EmptySquare)
\r
3367 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3372 if( texture_hdc != NULL ) {
\r
3373 DeleteDC( texture_hdc );
\r
3377 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3378 void fputDW(FILE *f, int x)
\r
3380 fputc(x & 255, f);
\r
3381 fputc(x>>8 & 255, f);
\r
3382 fputc(x>>16 & 255, f);
\r
3383 fputc(x>>24 & 255, f);
\r
3386 #define MAX_CLIPS 200 /* more than enough */
\r
3389 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3391 // HBITMAP bufferBitmap;
\r
3396 int w = 100, h = 50;
\r
3398 if(logo == NULL) {
\r
3399 if(!logoHeight) return;
\r
3400 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3402 // GetClientRect(hwndMain, &Rect);
\r
3403 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3404 // Rect.bottom-Rect.top+1);
\r
3405 tmphdc = CreateCompatibleDC(hdc);
\r
3406 hbm = SelectObject(tmphdc, logo);
\r
3407 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3411 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3412 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3413 SelectObject(tmphdc, hbm);
\r
3421 HDC hdc = GetDC(hwndMain);
\r
3422 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3423 if(appData.autoLogo) {
\r
3425 switch(gameMode) { // pick logos based on game mode
\r
3426 case IcsObserving:
\r
3427 whiteLogo = second.programLogo; // ICS logo
\r
3428 blackLogo = second.programLogo;
\r
3431 case IcsPlayingWhite:
\r
3432 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3433 blackLogo = second.programLogo; // ICS logo
\r
3435 case IcsPlayingBlack:
\r
3436 whiteLogo = second.programLogo; // ICS logo
\r
3437 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3439 case TwoMachinesPlay:
\r
3440 if(first.twoMachinesColor[0] == 'b') {
\r
3441 whiteLogo = second.programLogo;
\r
3442 blackLogo = first.programLogo;
\r
3445 case MachinePlaysWhite:
\r
3446 blackLogo = userLogo;
\r
3448 case MachinePlaysBlack:
\r
3449 whiteLogo = userLogo;
\r
3450 blackLogo = first.programLogo;
\r
3453 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3454 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3455 ReleaseDC(hwndMain, hdc);
\r
3460 UpdateLogos(int display)
\r
3461 { // called after loading new engine(s), in tourney or from menu
\r
3462 LoadLogo(&first, 0, FALSE);
\r
3463 LoadLogo(&second, 1, appData.icsActive);
\r
3464 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3465 if(display) DisplayLogos();
\r
3468 static HDC hdcSeek;
\r
3470 // [HGM] seekgraph
\r
3471 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3474 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3475 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3476 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3477 SelectObject( hdcSeek, hp );
\r
3480 // front-end wrapper for drawing functions to do rectangles
\r
3481 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3486 if (hdcSeek == NULL) {
\r
3487 hdcSeek = GetDC(hwndMain);
\r
3488 if (!appData.monoMode) {
\r
3489 SelectPalette(hdcSeek, hPal, FALSE);
\r
3490 RealizePalette(hdcSeek);
\r
3493 hp = SelectObject( hdcSeek, gridPen );
\r
3494 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3495 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3496 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3497 SelectObject( hdcSeek, hp );
\r
3500 // front-end wrapper for putting text in graph
\r
3501 void DrawSeekText(char *buf, int x, int y)
\r
3504 SetBkMode( hdcSeek, TRANSPARENT );
\r
3505 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3506 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3509 void DrawSeekDot(int x, int y, int color)
\r
3511 int square = color & 0x80;
\r
3512 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3513 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3516 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3517 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3519 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3520 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3521 SelectObject(hdcSeek, oldBrush);
\r
3525 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3527 static Board lastReq[2], lastDrawn[2];
\r
3528 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3529 static int lastDrawnFlipView = 0;
\r
3530 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3531 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3534 HBITMAP bufferBitmap;
\r
3535 HBITMAP oldBitmap;
\r
3537 HRGN clips[MAX_CLIPS];
\r
3538 ChessSquare dragged_piece = EmptySquare;
\r
3539 int nr = twoBoards*partnerUp;
\r
3541 /* I'm undecided on this - this function figures out whether a full
\r
3542 * repaint is necessary on its own, so there's no real reason to have the
\r
3543 * caller tell it that. I think this can safely be set to FALSE - but
\r
3544 * if we trust the callers not to request full repaints unnessesarily, then
\r
3545 * we could skip some clipping work. In other words, only request a full
\r
3546 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3547 * gamestart and similar) --Hawk
\r
3549 Boolean fullrepaint = repaint;
\r
3551 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3553 if( DrawPositionNeedsFullRepaint() ) {
\r
3554 fullrepaint = TRUE;
\r
3557 if (board == NULL) {
\r
3558 if (!lastReqValid[nr]) {
\r
3561 board = lastReq[nr];
\r
3563 CopyBoard(lastReq[nr], board);
\r
3564 lastReqValid[nr] = 1;
\r
3567 if (doingSizing) {
\r
3571 if (IsIconic(hwndMain)) {
\r
3575 if (hdc == NULL) {
\r
3576 hdc = GetDC(hwndMain);
\r
3577 if (!appData.monoMode) {
\r
3578 SelectPalette(hdc, hPal, FALSE);
\r
3579 RealizePalette(hdc);
\r
3583 releaseDC = FALSE;
\r
3586 /* Create some work-DCs */
\r
3587 hdcmem = CreateCompatibleDC(hdc);
\r
3588 tmphdc = CreateCompatibleDC(hdc);
\r
3590 /* If dragging is in progress, we temporarely remove the piece */
\r
3591 /* [HGM] or temporarily decrease count if stacked */
\r
3592 /* !! Moved to before board compare !! */
\r
3593 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3594 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3595 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3596 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3597 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3599 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3600 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3601 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3603 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3606 /* Figure out which squares need updating by comparing the
\r
3607 * newest board with the last drawn board and checking if
\r
3608 * flipping has changed.
\r
3610 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3611 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3612 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3613 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3614 SquareToPos(row, column, &x, &y);
\r
3615 clips[num_clips++] =
\r
3616 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3620 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3621 for (i=0; i<2; i++) {
\r
3622 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3623 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3624 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3625 lastDrawnHighlight.sq[i].y >= 0) {
\r
3626 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3627 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3628 clips[num_clips++] =
\r
3629 CreateRectRgn(x - lineGap, y - lineGap,
\r
3630 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3632 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3633 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3634 clips[num_clips++] =
\r
3635 CreateRectRgn(x - lineGap, y - lineGap,
\r
3636 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3640 for (i=0; i<2; i++) {
\r
3641 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3642 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3643 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3644 lastDrawnPremove.sq[i].y >= 0) {
\r
3645 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3646 lastDrawnPremove.sq[i].x, &x, &y);
\r
3647 clips[num_clips++] =
\r
3648 CreateRectRgn(x - lineGap, y - lineGap,
\r
3649 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3651 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3652 premoveHighlightInfo.sq[i].y >= 0) {
\r
3653 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3654 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3655 clips[num_clips++] =
\r
3656 CreateRectRgn(x - lineGap, y - lineGap,
\r
3657 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3661 } else { // nr == 1
\r
3662 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3663 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3664 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3665 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3666 for (i=0; i<2; i++) {
\r
3667 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3668 partnerHighlightInfo.sq[i].y >= 0) {
\r
3669 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3670 partnerHighlightInfo.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
3675 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3676 oldPartnerHighlight.sq[i].y >= 0) {
\r
3677 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3678 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3679 clips[num_clips++] =
\r
3680 CreateRectRgn(x - lineGap, y - lineGap,
\r
3681 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3686 fullrepaint = TRUE;
\r
3689 /* Create a buffer bitmap - this is the actual bitmap
\r
3690 * being written to. When all the work is done, we can
\r
3691 * copy it to the real DC (the screen). This avoids
\r
3692 * the problems with flickering.
\r
3694 GetClientRect(hwndMain, &Rect);
\r
3695 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3696 Rect.bottom-Rect.top+1);
\r
3697 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3698 if (!appData.monoMode) {
\r
3699 SelectPalette(hdcmem, hPal, FALSE);
\r
3702 /* Create clips for dragging */
\r
3703 if (!fullrepaint) {
\r
3704 if (dragInfo.from.x >= 0) {
\r
3705 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3706 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3708 if (dragInfo.start.x >= 0) {
\r
3709 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3710 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3712 if (dragInfo.pos.x >= 0) {
\r
3713 x = dragInfo.pos.x - squareSize / 2;
\r
3714 y = dragInfo.pos.y - squareSize / 2;
\r
3715 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3717 if (dragInfo.lastpos.x >= 0) {
\r
3718 x = dragInfo.lastpos.x - squareSize / 2;
\r
3719 y = dragInfo.lastpos.y - squareSize / 2;
\r
3720 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3724 /* Are we animating a move?
\r
3726 * - remove the piece from the board (temporarely)
\r
3727 * - calculate the clipping region
\r
3729 if (!fullrepaint) {
\r
3730 if (animInfo.piece != EmptySquare) {
\r
3731 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3732 x = boardRect.left + animInfo.lastpos.x;
\r
3733 y = boardRect.top + animInfo.lastpos.y;
\r
3734 x2 = boardRect.left + animInfo.pos.x;
\r
3735 y2 = boardRect.top + animInfo.pos.y;
\r
3736 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3737 /* Slight kludge. The real problem is that after AnimateMove is
\r
3738 done, the position on the screen does not match lastDrawn.
\r
3739 This currently causes trouble only on e.p. captures in
\r
3740 atomic, where the piece moves to an empty square and then
\r
3741 explodes. The old and new positions both had an empty square
\r
3742 at the destination, but animation has drawn a piece there and
\r
3743 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3744 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3748 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3749 if (num_clips == 0)
\r
3750 fullrepaint = TRUE;
\r
3752 /* Set clipping on the memory DC */
\r
3753 if (!fullrepaint) {
\r
3754 SelectClipRgn(hdcmem, clips[0]);
\r
3755 for (x = 1; x < num_clips; x++) {
\r
3756 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3757 abort(); // this should never ever happen!
\r
3761 /* Do all the drawing to the memory DC */
\r
3762 if(explodeInfo.radius) { // [HGM] atomic
\r
3764 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3765 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3766 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3767 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3768 x += squareSize/2;
\r
3769 y += squareSize/2;
\r
3770 if(!fullrepaint) {
\r
3771 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3772 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3774 DrawGridOnDC(hdcmem);
\r
3775 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3776 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3777 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3778 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3779 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3780 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3781 SelectObject(hdcmem, oldBrush);
\r
3783 DrawGridOnDC(hdcmem);
\r
3784 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3785 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3786 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3788 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3789 oldPartnerHighlight = partnerHighlightInfo;
\r
3791 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3793 if(nr == 0) // [HGM] dual: markers only on left board
\r
3794 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3795 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3796 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3797 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3798 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3799 SquareToPos(row, column, &x, &y);
\r
3800 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3801 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3802 SelectObject(hdcmem, oldBrush);
\r
3807 if( appData.highlightMoveWithArrow ) {
\r
3808 DrawArrowHighlight(hdcmem);
\r
3811 DrawCoordsOnDC(hdcmem);
\r
3813 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3814 /* to make sure lastDrawn contains what is actually drawn */
\r
3816 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3817 if (dragged_piece != EmptySquare) {
\r
3818 /* [HGM] or restack */
\r
3819 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3820 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3822 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3823 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3824 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3825 x = dragInfo.pos.x - squareSize / 2;
\r
3826 y = dragInfo.pos.y - squareSize / 2;
\r
3827 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3828 ((int) dragInfo.piece < (int) BlackPawn),
\r
3829 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3832 /* Put the animated piece back into place and draw it */
\r
3833 if (animInfo.piece != EmptySquare) {
\r
3834 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3835 x = boardRect.left + animInfo.pos.x;
\r
3836 y = boardRect.top + animInfo.pos.y;
\r
3837 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3838 ((int) animInfo.piece < (int) BlackPawn),
\r
3839 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3842 /* Release the bufferBitmap by selecting in the old bitmap
\r
3843 * and delete the memory DC
\r
3845 SelectObject(hdcmem, oldBitmap);
\r
3848 /* Set clipping on the target DC */
\r
3849 if (!fullrepaint) {
\r
3850 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3852 GetRgnBox(clips[x], &rect);
\r
3853 DeleteObject(clips[x]);
\r
3854 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3855 rect.right + wpMain.width/2, rect.bottom);
\r
3857 SelectClipRgn(hdc, clips[0]);
\r
3858 for (x = 1; x < num_clips; x++) {
\r
3859 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3860 abort(); // this should never ever happen!
\r
3864 /* Copy the new bitmap onto the screen in one go.
\r
3865 * This way we avoid any flickering
\r
3867 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3868 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3869 boardRect.right - boardRect.left,
\r
3870 boardRect.bottom - boardRect.top,
\r
3871 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3872 if(saveDiagFlag) {
\r
3873 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3874 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3876 GetObject(bufferBitmap, sizeof(b), &b);
\r
3877 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3878 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3879 bih.biWidth = b.bmWidth;
\r
3880 bih.biHeight = b.bmHeight;
\r
3882 bih.biBitCount = b.bmBitsPixel;
\r
3883 bih.biCompression = 0;
\r
3884 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3885 bih.biXPelsPerMeter = 0;
\r
3886 bih.biYPelsPerMeter = 0;
\r
3887 bih.biClrUsed = 0;
\r
3888 bih.biClrImportant = 0;
\r
3889 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3890 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3891 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3892 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3894 wb = b.bmWidthBytes;
\r
3896 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3897 int k = ((int*) pData)[i];
\r
3898 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3899 if(j >= 16) break;
\r
3901 if(j >= nrColors) nrColors = j+1;
\r
3903 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3905 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3906 for(w=0; w<(wb>>2); w+=2) {
\r
3907 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3908 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3909 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3910 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3911 pData[p++] = m | j<<4;
\r
3913 while(p&3) pData[p++] = 0;
\r
3916 wb = ((wb+31)>>5)<<2;
\r
3918 // write BITMAPFILEHEADER
\r
3919 fprintf(diagFile, "BM");
\r
3920 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3921 fputDW(diagFile, 0);
\r
3922 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3923 // write BITMAPINFOHEADER
\r
3924 fputDW(diagFile, 40);
\r
3925 fputDW(diagFile, b.bmWidth);
\r
3926 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3927 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3928 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3929 fputDW(diagFile, 0);
\r
3930 fputDW(diagFile, 0);
\r
3931 fputDW(diagFile, 0);
\r
3932 fputDW(diagFile, 0);
\r
3933 fputDW(diagFile, 0);
\r
3934 fputDW(diagFile, 0);
\r
3935 // write color table
\r
3937 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3938 // write bitmap data
\r
3939 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3940 fputc(pData[i], diagFile);
\r
3945 SelectObject(tmphdc, oldBitmap);
\r
3947 /* Massive cleanup */
\r
3948 for (x = 0; x < num_clips; x++)
\r
3949 DeleteObject(clips[x]);
\r
3952 DeleteObject(bufferBitmap);
\r
3955 ReleaseDC(hwndMain, hdc);
\r
3957 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3959 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3961 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3964 /* CopyBoard(lastDrawn, board);*/
\r
3965 lastDrawnHighlight = highlightInfo;
\r
3966 lastDrawnPremove = premoveHighlightInfo;
\r
3967 lastDrawnFlipView = flipView;
\r
3968 lastDrawnValid[nr] = 1;
\r
3971 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3976 saveDiagFlag = 1; diagFile = f;
\r
3977 HDCDrawPosition(NULL, TRUE, NULL);
\r
3985 /*---------------------------------------------------------------------------*\
\r
3986 | CLIENT PAINT PROCEDURE
\r
3987 | This is the main event-handler for the WM_PAINT message.
\r
3989 \*---------------------------------------------------------------------------*/
\r
3991 PaintProc(HWND hwnd)
\r
3997 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3998 if (IsIconic(hwnd)) {
\r
3999 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4001 if (!appData.monoMode) {
\r
4002 SelectPalette(hdc, hPal, FALSE);
\r
4003 RealizePalette(hdc);
\r
4005 HDCDrawPosition(hdc, 1, NULL);
\r
4006 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4007 flipView = !flipView; partnerUp = !partnerUp;
\r
4008 HDCDrawPosition(hdc, 1, NULL);
\r
4009 flipView = !flipView; partnerUp = !partnerUp;
\r
4012 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4013 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4014 ETO_CLIPPED|ETO_OPAQUE,
\r
4015 &messageRect, messageText, strlen(messageText), NULL);
\r
4016 SelectObject(hdc, oldFont);
\r
4017 DisplayBothClocks();
\r
4020 EndPaint(hwnd,&ps);
\r
4028 * If the user selects on a border boundary, return -1; if off the board,
\r
4029 * return -2. Otherwise map the event coordinate to the square.
\r
4030 * The offset boardRect.left or boardRect.top must already have been
\r
4031 * subtracted from x.
\r
4033 int EventToSquare(x, limit)
\r
4041 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4043 x /= (squareSize + lineGap);
\r
4055 DropEnable dropEnables[] = {
\r
4056 { 'P', DP_Pawn, N_("Pawn") },
\r
4057 { 'N', DP_Knight, N_("Knight") },
\r
4058 { 'B', DP_Bishop, N_("Bishop") },
\r
4059 { 'R', DP_Rook, N_("Rook") },
\r
4060 { 'Q', DP_Queen, N_("Queen") },
\r
4064 SetupDropMenu(HMENU hmenu)
\r
4066 int i, count, enable;
\r
4068 extern char white_holding[], black_holding[];
\r
4069 char item[MSG_SIZ];
\r
4071 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4072 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4073 dropEnables[i].piece);
\r
4075 while (p && *p++ == dropEnables[i].piece) count++;
\r
4076 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4077 enable = count > 0 || !appData.testLegality
\r
4078 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4079 && !appData.icsActive);
\r
4080 ModifyMenu(hmenu, dropEnables[i].command,
\r
4081 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4082 dropEnables[i].command, item);
\r
4086 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4088 dragInfo.lastpos.x = boardRect.left + x;
\r
4089 dragInfo.lastpos.y = boardRect.top + y;
\r
4090 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4091 dragInfo.from.x = fromX;
\r
4092 dragInfo.from.y = fromY;
\r
4093 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4094 dragInfo.start = dragInfo.from;
\r
4095 SetCapture(hwndMain);
\r
4098 void DragPieceEnd(int x, int y)
\r
4101 dragInfo.start.x = dragInfo.start.y = -1;
\r
4102 dragInfo.from = dragInfo.start;
\r
4103 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4106 void ChangeDragPiece(ChessSquare piece)
\r
4108 dragInfo.piece = piece;
\r
4111 /* Event handler for mouse messages */
\r
4113 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4117 static int recursive = 0;
\r
4119 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4122 if (message == WM_MBUTTONUP) {
\r
4123 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4124 to the middle button: we simulate pressing the left button too!
\r
4126 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4127 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4133 pt.x = LOWORD(lParam);
\r
4134 pt.y = HIWORD(lParam);
\r
4135 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4136 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4137 if (!flipView && y >= 0) {
\r
4138 y = BOARD_HEIGHT - 1 - y;
\r
4140 if (flipView && x >= 0) {
\r
4141 x = BOARD_WIDTH - 1 - x;
\r
4144 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4146 switch (message) {
\r
4147 case WM_LBUTTONDOWN:
\r
4148 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4149 ClockClick(flipClock); break;
\r
4150 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4151 ClockClick(!flipClock); break;
\r
4153 dragInfo.start.x = dragInfo.start.y = -1;
\r
4154 dragInfo.from = dragInfo.start;
\r
4155 if(fromX == -1 && frozen) { // not sure where this is for
\r
4156 fromX = fromY = -1;
\r
4157 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4160 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4161 DrawPosition(TRUE, NULL);
\r
4164 case WM_LBUTTONUP:
\r
4165 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4166 DrawPosition(TRUE, NULL);
\r
4169 case WM_MOUSEMOVE:
\r
4170 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4171 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4172 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4173 if ((appData.animateDragging || appData.highlightDragging)
\r
4174 && (wParam & MK_LBUTTON)
\r
4175 && dragInfo.from.x >= 0)
\r
4177 BOOL full_repaint = FALSE;
\r
4179 if (appData.animateDragging) {
\r
4180 dragInfo.pos = pt;
\r
4182 if (appData.highlightDragging) {
\r
4183 SetHighlights(fromX, fromY, x, y);
\r
4184 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4185 full_repaint = TRUE;
\r
4189 DrawPosition( full_repaint, NULL);
\r
4191 dragInfo.lastpos = dragInfo.pos;
\r
4195 case WM_MOUSEWHEEL: // [DM]
\r
4196 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4197 /* Mouse Wheel is being rolled forward
\r
4198 * Play moves forward
\r
4200 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4201 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4202 /* Mouse Wheel is being rolled backward
\r
4203 * Play moves backward
\r
4205 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4206 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4210 case WM_MBUTTONUP:
\r
4211 case WM_RBUTTONUP:
\r
4213 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4216 case WM_MBUTTONDOWN:
\r
4217 case WM_RBUTTONDOWN:
\r
4220 fromX = fromY = -1;
\r
4221 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4222 dragInfo.start.x = dragInfo.start.y = -1;
\r
4223 dragInfo.from = dragInfo.start;
\r
4224 dragInfo.lastpos = dragInfo.pos;
\r
4225 if (appData.highlightDragging) {
\r
4226 ClearHighlights();
\r
4229 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4230 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4231 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4232 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4233 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4237 DrawPosition(TRUE, NULL);
\r
4239 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4242 if (message == WM_MBUTTONDOWN) {
\r
4243 buttonCount = 3; /* even if system didn't think so */
\r
4244 if (wParam & MK_SHIFT)
\r
4245 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4247 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4248 } else { /* message == WM_RBUTTONDOWN */
\r
4249 /* Just have one menu, on the right button. Windows users don't
\r
4250 think to try the middle one, and sometimes other software steals
\r
4251 it, or it doesn't really exist. */
\r
4252 if(gameInfo.variant != VariantShogi)
\r
4253 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4255 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4259 SetCapture(hwndMain);
4262 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4263 SetupDropMenu(hmenu);
\r
4264 MenuPopup(hwnd, pt, hmenu, -1);
\r
4274 /* Preprocess messages for buttons in main window */
\r
4276 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4278 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4281 for (i=0; i<N_BUTTONS; i++) {
\r
4282 if (buttonDesc[i].id == id) break;
\r
4284 if (i == N_BUTTONS) return 0;
\r
4285 switch (message) {
\r
4290 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4291 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4298 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4301 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4302 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4303 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4304 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4306 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4308 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4309 TypeInEvent((char)wParam);
\r
4315 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4318 /* Process messages for Promotion dialog box */
\r
4320 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4324 switch (message) {
\r
4325 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4326 /* Center the dialog over the application window */
\r
4327 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4328 Translate(hDlg, DLG_PromotionKing);
\r
4329 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4330 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4331 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4332 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4333 SW_SHOW : SW_HIDE);
\r
4334 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4335 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4336 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4337 PieceToChar(WhiteAngel) != '~') ||
\r
4338 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4339 PieceToChar(BlackAngel) != '~') ) ?
\r
4340 SW_SHOW : SW_HIDE);
\r
4341 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4342 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4343 PieceToChar(WhiteMarshall) != '~') ||
\r
4344 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4345 PieceToChar(BlackMarshall) != '~') ) ?
\r
4346 SW_SHOW : SW_HIDE);
\r
4347 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4348 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4349 gameInfo.variant != VariantShogi ?
\r
4350 SW_SHOW : SW_HIDE);
\r
4351 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4352 gameInfo.variant != VariantShogi ?
\r
4353 SW_SHOW : SW_HIDE);
\r
4354 if(gameInfo.variant == VariantShogi) {
\r
4355 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4356 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4357 SetWindowText(hDlg, "Promote?");
\r
4359 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4360 gameInfo.variant == VariantSuper ?
\r
4361 SW_SHOW : SW_HIDE);
\r
4364 case WM_COMMAND: /* message: received a command */
\r
4365 switch (LOWORD(wParam)) {
\r
4367 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4368 ClearHighlights();
\r
4369 DrawPosition(FALSE, NULL);
\r
4372 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4375 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4378 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4379 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4382 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4383 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4385 case PB_Chancellor:
\r
4386 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4388 case PB_Archbishop:
\r
4389 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4392 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4397 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4398 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4399 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4400 fromX = fromY = -1;
\r
4401 if (!appData.highlightLastMove) {
\r
4402 ClearHighlights();
\r
4403 DrawPosition(FALSE, NULL);
\r
4410 /* Pop up promotion dialog */
\r
4412 PromotionPopup(HWND hwnd)
\r
4416 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4417 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4418 hwnd, (DLGPROC)lpProc);
\r
4419 FreeProcInstance(lpProc);
\r
4425 DrawPosition(TRUE, NULL);
\r
4426 PromotionPopup(hwndMain);
\r
4429 /* Toggle ShowThinking */
\r
4431 ToggleShowThinking()
\r
4433 appData.showThinking = !appData.showThinking;
\r
4434 ShowThinkingEvent();
\r
4438 LoadGameDialog(HWND hwnd, char* title)
\r
4442 char fileTitle[MSG_SIZ];
\r
4443 f = OpenFileDialog(hwnd, "rb", "",
\r
4444 appData.oldSaveStyle ? "gam" : "pgn",
\r
4446 title, &number, fileTitle, NULL);
\r
4448 cmailMsgLoaded = FALSE;
\r
4449 if (number == 0) {
\r
4450 int error = GameListBuild(f);
\r
4452 DisplayError(_("Cannot build game list"), error);
\r
4453 } else if (!ListEmpty(&gameList) &&
\r
4454 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4455 GameListPopUp(f, fileTitle);
\r
4458 GameListDestroy();
\r
4461 LoadGame(f, number, fileTitle, FALSE);
\r
4465 int get_term_width()
\r
4470 HFONT hfont, hold_font;
\r
4475 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4479 // get the text metrics
\r
4480 hdc = GetDC(hText);
\r
4481 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4482 if (consoleCF.dwEffects & CFE_BOLD)
\r
4483 lf.lfWeight = FW_BOLD;
\r
4484 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4485 lf.lfItalic = TRUE;
\r
4486 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4487 lf.lfStrikeOut = TRUE;
\r
4488 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4489 lf.lfUnderline = TRUE;
\r
4490 hfont = CreateFontIndirect(&lf);
\r
4491 hold_font = SelectObject(hdc, hfont);
\r
4492 GetTextMetrics(hdc, &tm);
\r
4493 SelectObject(hdc, hold_font);
\r
4494 DeleteObject(hfont);
\r
4495 ReleaseDC(hText, hdc);
\r
4497 // get the rectangle
\r
4498 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4500 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4503 void UpdateICSWidth(HWND hText)
\r
4505 LONG old_width, new_width;
\r
4507 new_width = get_term_width(hText, FALSE);
\r
4508 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4509 if (new_width != old_width)
\r
4511 ics_update_width(new_width);
\r
4512 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4517 ChangedConsoleFont()
\r
4520 CHARRANGE tmpsel, sel;
\r
4521 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4522 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4523 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4526 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4527 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4528 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4529 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4530 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4531 * size. This was undocumented in the version of MSVC++ that I had
\r
4532 * when I wrote the code, but is apparently documented now.
\r
4534 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4535 cfmt.bCharSet = f->lf.lfCharSet;
\r
4536 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4537 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4538 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4539 /* Why are the following seemingly needed too? */
\r
4540 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4541 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4542 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4544 tmpsel.cpMax = -1; /*999999?*/
\r
4545 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4546 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4547 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4548 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4550 paraf.cbSize = sizeof(paraf);
\r
4551 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4552 paraf.dxStartIndent = 0;
\r
4553 paraf.dxOffset = WRAP_INDENT;
\r
4554 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4555 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4556 UpdateICSWidth(hText);
\r
4559 /*---------------------------------------------------------------------------*\
\r
4561 * Window Proc for main window
\r
4563 \*---------------------------------------------------------------------------*/
\r
4565 /* Process messages for main window, etc. */
\r
4567 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4570 int wmId, wmEvent;
\r
4574 char fileTitle[MSG_SIZ];
\r
4575 char buf[MSG_SIZ];
\r
4576 static SnapData sd;
\r
4577 static int peek=0;
\r
4579 switch (message) {
\r
4581 case WM_PAINT: /* message: repaint portion of window */
\r
4585 case WM_ERASEBKGND:
\r
4586 if (IsIconic(hwnd)) {
\r
4587 /* Cheat; change the message */
\r
4588 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4590 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4594 case WM_LBUTTONDOWN:
\r
4595 case WM_MBUTTONDOWN:
\r
4596 case WM_RBUTTONDOWN:
\r
4597 case WM_LBUTTONUP:
\r
4598 case WM_MBUTTONUP:
\r
4599 case WM_RBUTTONUP:
\r
4600 case WM_MOUSEMOVE:
\r
4601 case WM_MOUSEWHEEL:
\r
4602 MouseEvent(hwnd, message, wParam, lParam);
\r
4606 if((char)wParam == '\b') {
\r
4607 ForwardEvent(); peek = 0;
\r
4610 JAWS_KBUP_NAVIGATION
\r
4615 if((char)wParam == '\b') {
\r
4616 if(!peek) BackwardEvent(), peek = 1;
\r
4619 JAWS_KBDOWN_NAVIGATION
\r
4625 JAWS_ALT_INTERCEPT
\r
4627 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4628 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4629 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4630 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4632 SendMessage(h, message, wParam, lParam);
\r
4633 } else if(lParam != KF_REPEAT) {
\r
4634 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4635 TypeInEvent((char)wParam);
\r
4636 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4637 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4642 case WM_PALETTECHANGED:
\r
4643 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4645 HDC hdc = GetDC(hwndMain);
\r
4646 SelectPalette(hdc, hPal, TRUE);
\r
4647 nnew = RealizePalette(hdc);
\r
4649 paletteChanged = TRUE;
\r
4650 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4652 ReleaseDC(hwnd, hdc);
\r
4656 case WM_QUERYNEWPALETTE:
\r
4657 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4659 HDC hdc = GetDC(hwndMain);
\r
4660 paletteChanged = FALSE;
\r
4661 SelectPalette(hdc, hPal, FALSE);
\r
4662 nnew = RealizePalette(hdc);
\r
4664 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4666 ReleaseDC(hwnd, hdc);
\r
4671 case WM_COMMAND: /* message: command from application menu */
\r
4672 wmId = LOWORD(wParam);
\r
4673 wmEvent = HIWORD(wParam);
\r
4678 SAY("new game enter a move to play against the computer with white");
\r
4681 case IDM_NewGameFRC:
\r
4682 if( NewGameFRC() == 0 ) {
\r
4687 case IDM_NewVariant:
\r
4688 NewVariantPopup(hwnd);
\r
4691 case IDM_LoadGame:
\r
4692 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4695 case IDM_LoadNextGame:
\r
4699 case IDM_LoadPrevGame:
\r
4703 case IDM_ReloadGame:
\r
4707 case IDM_LoadPosition:
\r
4708 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4709 Reset(FALSE, TRUE);
\r
4712 f = OpenFileDialog(hwnd, "rb", "",
\r
4713 appData.oldSaveStyle ? "pos" : "fen",
\r
4715 _("Load Position from File"), &number, fileTitle, NULL);
\r
4717 LoadPosition(f, number, fileTitle);
\r
4721 case IDM_LoadNextPosition:
\r
4722 ReloadPosition(1);
\r
4725 case IDM_LoadPrevPosition:
\r
4726 ReloadPosition(-1);
\r
4729 case IDM_ReloadPosition:
\r
4730 ReloadPosition(0);
\r
4733 case IDM_SaveGame:
\r
4734 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4735 f = OpenFileDialog(hwnd, "a", defName,
\r
4736 appData.oldSaveStyle ? "gam" : "pgn",
\r
4738 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4740 SaveGame(f, 0, "");
\r
4744 case IDM_SavePosition:
\r
4745 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4746 f = OpenFileDialog(hwnd, "a", defName,
\r
4747 appData.oldSaveStyle ? "pos" : "fen",
\r
4749 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4751 SavePosition(f, 0, "");
\r
4755 case IDM_SaveDiagram:
\r
4756 defName = "diagram";
\r
4757 f = OpenFileDialog(hwnd, "wb", defName,
\r
4760 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4766 case IDM_CopyGame:
\r
4767 CopyGameToClipboard();
\r
4770 case IDM_PasteGame:
\r
4771 PasteGameFromClipboard();
\r
4774 case IDM_CopyGameListToClipboard:
\r
4775 CopyGameListToClipboard();
\r
4778 /* [AS] Autodetect FEN or PGN data */
\r
4779 case IDM_PasteAny:
\r
4780 PasteGameOrFENFromClipboard();
\r
4783 /* [AS] Move history */
\r
4784 case IDM_ShowMoveHistory:
\r
4785 if( MoveHistoryIsUp() ) {
\r
4786 MoveHistoryPopDown();
\r
4789 MoveHistoryPopUp();
\r
4793 /* [AS] Eval graph */
\r
4794 case IDM_ShowEvalGraph:
\r
4795 if( EvalGraphIsUp() ) {
\r
4796 EvalGraphPopDown();
\r
4800 SetFocus(hwndMain);
\r
4804 /* [AS] Engine output */
\r
4805 case IDM_ShowEngineOutput:
\r
4806 if( EngineOutputIsUp() ) {
\r
4807 EngineOutputPopDown();
\r
4810 EngineOutputPopUp();
\r
4814 /* [AS] User adjudication */
\r
4815 case IDM_UserAdjudication_White:
\r
4816 UserAdjudicationEvent( +1 );
\r
4819 case IDM_UserAdjudication_Black:
\r
4820 UserAdjudicationEvent( -1 );
\r
4823 case IDM_UserAdjudication_Draw:
\r
4824 UserAdjudicationEvent( 0 );
\r
4827 /* [AS] Game list options dialog */
\r
4828 case IDM_GameListOptions:
\r
4829 GameListOptions();
\r
4836 case IDM_CopyPosition:
\r
4837 CopyFENToClipboard();
\r
4840 case IDM_PastePosition:
\r
4841 PasteFENFromClipboard();
\r
4844 case IDM_MailMove:
\r
4848 case IDM_ReloadCMailMsg:
\r
4849 Reset(TRUE, TRUE);
\r
4850 ReloadCmailMsgEvent(FALSE);
\r
4853 case IDM_Minimize:
\r
4854 ShowWindow(hwnd, SW_MINIMIZE);
\r
4861 case IDM_MachineWhite:
\r
4862 MachineWhiteEvent();
\r
4864 * refresh the tags dialog only if it's visible
\r
4866 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4868 tags = PGNTags(&gameInfo);
\r
4869 TagsPopUp(tags, CmailMsg());
\r
4872 SAY("computer starts playing white");
\r
4875 case IDM_MachineBlack:
\r
4876 MachineBlackEvent();
\r
4878 * refresh the tags dialog only if it's visible
\r
4880 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4882 tags = PGNTags(&gameInfo);
\r
4883 TagsPopUp(tags, CmailMsg());
\r
4886 SAY("computer starts playing black");
\r
4889 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4890 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4893 case IDM_TwoMachines:
\r
4894 TwoMachinesEvent();
\r
4896 * refresh the tags dialog only if it's visible
\r
4898 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4900 tags = PGNTags(&gameInfo);
\r
4901 TagsPopUp(tags, CmailMsg());
\r
4904 SAY("computer starts playing both sides");
\r
4907 case IDM_AnalysisMode:
\r
4908 if (!first.analysisSupport) {
\r
4909 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4910 DisplayError(buf, 0);
\r
4912 SAY("analyzing current position");
\r
4913 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4914 if (appData.icsActive) {
\r
4915 if (gameMode != IcsObserving) {
\r
4916 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4917 DisplayError(buf, 0);
\r
4918 /* secure check */
\r
4919 if (appData.icsEngineAnalyze) {
\r
4920 if (appData.debugMode)
\r
4921 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4922 ExitAnalyzeMode();
\r
4928 /* if enable, user want disable icsEngineAnalyze */
\r
4929 if (appData.icsEngineAnalyze) {
\r
4930 ExitAnalyzeMode();
\r
4934 appData.icsEngineAnalyze = TRUE;
\r
4935 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4938 if (!appData.showThinking) ToggleShowThinking();
\r
4939 AnalyzeModeEvent();
\r
4943 case IDM_AnalyzeFile:
\r
4944 if (!first.analysisSupport) {
\r
4945 char buf[MSG_SIZ];
\r
4946 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4947 DisplayError(buf, 0);
\r
4949 if (!appData.showThinking) ToggleShowThinking();
\r
4950 AnalyzeFileEvent();
\r
4951 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4952 AnalysisPeriodicEvent(1);
\r
4956 case IDM_IcsClient:
\r
4960 case IDM_EditGame:
\r
4961 case IDM_EditGame2:
\r
4966 case IDM_EditPosition:
\r
4967 case IDM_EditPosition2:
\r
4968 EditPositionEvent();
\r
4969 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4972 case IDM_Training:
\r
4976 case IDM_ShowGameList:
\r
4977 ShowGameListProc();
\r
4980 case IDM_EditProgs1:
\r
4981 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4984 case IDM_EditProgs2:
\r
4985 LoadEnginePopUp(hwndMain);
\r
4986 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4989 case IDM_EditServers:
\r
4990 EditTagsPopUp(icsNames, &icsNames);
\r
4993 case IDM_EditTags:
\r
4998 case IDM_EditBook:
\r
5002 case IDM_EditComment:
\r
5004 if (commentUp && editComment) {
\r
5007 EditCommentEvent();
\r
5027 case IDM_CallFlag:
\r
5047 case IDM_StopObserving:
\r
5048 StopObservingEvent();
\r
5051 case IDM_StopExamining:
\r
5052 StopExaminingEvent();
\r
5056 UploadGameEvent();
\r
5059 case IDM_TypeInMove:
\r
5060 TypeInEvent('\000');
\r
5063 case IDM_TypeInName:
\r
5064 PopUpNameDialog('\000');
\r
5067 case IDM_Backward:
\r
5069 SetFocus(hwndMain);
\r
5076 SetFocus(hwndMain);
\r
5081 SetFocus(hwndMain);
\r
5086 SetFocus(hwndMain);
\r
5089 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5090 case OPT_GameListPrev:
\r
5091 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5095 RevertEvent(FALSE);
\r
5098 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5099 RevertEvent(TRUE);
\r
5102 case IDM_TruncateGame:
\r
5103 TruncateGameEvent();
\r
5110 case IDM_RetractMove:
\r
5111 RetractMoveEvent();
\r
5114 case IDM_FlipView:
\r
5115 flipView = !flipView;
\r
5116 DrawPosition(FALSE, NULL);
\r
5119 case IDM_FlipClock:
\r
5120 flipClock = !flipClock;
\r
5121 DisplayBothClocks();
\r
5125 case IDM_MuteSounds:
\r
5126 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5127 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5128 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5131 case IDM_GeneralOptions:
\r
5132 GeneralOptionsPopup(hwnd);
\r
5133 DrawPosition(TRUE, NULL);
\r
5136 case IDM_BoardOptions:
\r
5137 BoardOptionsPopup(hwnd);
\r
5140 case IDM_EnginePlayOptions:
\r
5141 EnginePlayOptionsPopup(hwnd);
\r
5144 case IDM_Engine1Options:
\r
5145 EngineOptionsPopup(hwnd, &first);
\r
5148 case IDM_Engine2Options:
\r
5150 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5151 EngineOptionsPopup(hwnd, &second);
\r
5154 case IDM_OptionsUCI:
\r
5155 UciOptionsPopup(hwnd);
\r
5159 TourneyPopup(hwnd);
\r
5162 case IDM_IcsOptions:
\r
5163 IcsOptionsPopup(hwnd);
\r
5167 FontsOptionsPopup(hwnd);
\r
5171 SoundOptionsPopup(hwnd);
\r
5174 case IDM_CommPort:
\r
5175 CommPortOptionsPopup(hwnd);
\r
5178 case IDM_LoadOptions:
\r
5179 LoadOptionsPopup(hwnd);
\r
5182 case IDM_SaveOptions:
\r
5183 SaveOptionsPopup(hwnd);
\r
5186 case IDM_TimeControl:
\r
5187 TimeControlOptionsPopup(hwnd);
\r
5190 case IDM_SaveSettings:
\r
5191 SaveSettings(settingsFileName);
\r
5194 case IDM_SaveSettingsOnExit:
\r
5195 saveSettingsOnExit = !saveSettingsOnExit;
\r
5196 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5197 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5198 MF_CHECKED : MF_UNCHECKED));
\r
5209 case IDM_AboutGame:
\r
5214 appData.debugMode = !appData.debugMode;
\r
5215 if (appData.debugMode) {
\r
5216 char dir[MSG_SIZ];
\r
5217 GetCurrentDirectory(MSG_SIZ, dir);
\r
5218 SetCurrentDirectory(installDir);
\r
5219 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5220 SetCurrentDirectory(dir);
\r
5221 setbuf(debugFP, NULL);
\r
5228 case IDM_HELPCONTENTS:
\r
5229 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5230 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5231 MessageBox (GetFocus(),
\r
5232 _("Unable to activate help"),
\r
5233 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5237 case IDM_HELPSEARCH:
\r
5238 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5239 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5240 MessageBox (GetFocus(),
\r
5241 _("Unable to activate help"),
\r
5242 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5246 case IDM_HELPHELP:
\r
5247 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5248 MessageBox (GetFocus(),
\r
5249 _("Unable to activate help"),
\r
5250 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5255 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5257 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5258 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5259 FreeProcInstance(lpProc);
\r
5262 case IDM_DirectCommand1:
\r
5263 AskQuestionEvent(_("Direct Command"),
\r
5264 _("Send to chess program:"), "", "1");
\r
5266 case IDM_DirectCommand2:
\r
5267 AskQuestionEvent(_("Direct Command"),
\r
5268 _("Send to second chess program:"), "", "2");
\r
5271 case EP_WhitePawn:
\r
5272 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5273 fromX = fromY = -1;
\r
5276 case EP_WhiteKnight:
\r
5277 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5278 fromX = fromY = -1;
\r
5281 case EP_WhiteBishop:
\r
5282 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5283 fromX = fromY = -1;
\r
5286 case EP_WhiteRook:
\r
5287 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5288 fromX = fromY = -1;
\r
5291 case EP_WhiteQueen:
\r
5292 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5293 fromX = fromY = -1;
\r
5296 case EP_WhiteFerz:
\r
5297 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5298 fromX = fromY = -1;
\r
5301 case EP_WhiteWazir:
\r
5302 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5303 fromX = fromY = -1;
\r
5306 case EP_WhiteAlfil:
\r
5307 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5308 fromX = fromY = -1;
\r
5311 case EP_WhiteCannon:
\r
5312 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5313 fromX = fromY = -1;
\r
5316 case EP_WhiteCardinal:
\r
5317 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5318 fromX = fromY = -1;
\r
5321 case EP_WhiteMarshall:
\r
5322 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5323 fromX = fromY = -1;
\r
5326 case EP_WhiteKing:
\r
5327 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5328 fromX = fromY = -1;
\r
5331 case EP_BlackPawn:
\r
5332 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5333 fromX = fromY = -1;
\r
5336 case EP_BlackKnight:
\r
5337 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5338 fromX = fromY = -1;
\r
5341 case EP_BlackBishop:
\r
5342 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5343 fromX = fromY = -1;
\r
5346 case EP_BlackRook:
\r
5347 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5348 fromX = fromY = -1;
\r
5351 case EP_BlackQueen:
\r
5352 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5353 fromX = fromY = -1;
\r
5356 case EP_BlackFerz:
\r
5357 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5358 fromX = fromY = -1;
\r
5361 case EP_BlackWazir:
\r
5362 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5363 fromX = fromY = -1;
\r
5366 case EP_BlackAlfil:
\r
5367 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5368 fromX = fromY = -1;
\r
5371 case EP_BlackCannon:
\r
5372 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5373 fromX = fromY = -1;
\r
5376 case EP_BlackCardinal:
\r
5377 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5378 fromX = fromY = -1;
\r
5381 case EP_BlackMarshall:
\r
5382 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5383 fromX = fromY = -1;
\r
5386 case EP_BlackKing:
\r
5387 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5388 fromX = fromY = -1;
\r
5391 case EP_EmptySquare:
\r
5392 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5393 fromX = fromY = -1;
\r
5396 case EP_ClearBoard:
\r
5397 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5398 fromX = fromY = -1;
\r
5402 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5403 fromX = fromY = -1;
\r
5407 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5408 fromX = fromY = -1;
\r
5412 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5413 fromX = fromY = -1;
\r
5417 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5418 fromX = fromY = -1;
\r
5422 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5423 fromX = fromY = -1;
\r
5427 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5428 fromX = fromY = -1;
\r
5432 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5433 fromX = fromY = -1;
\r
5437 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5438 fromX = fromY = -1;
\r
5442 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5443 fromX = fromY = -1;
\r
5447 barbaric = 0; appData.language = "";
\r
5448 TranslateMenus(0);
\r
5449 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5450 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5451 lastChecked = wmId;
\r
5455 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5456 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5457 TranslateMenus(0);
\r
5458 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5459 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5460 lastChecked = wmId;
\r
5463 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5469 case CLOCK_TIMER_ID:
\r
5470 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5471 clockTimerEvent = 0;
\r
5472 DecrementClocks(); /* call into back end */
\r
5474 case LOAD_GAME_TIMER_ID:
\r
5475 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5476 loadGameTimerEvent = 0;
\r
5477 AutoPlayGameLoop(); /* call into back end */
\r
5479 case ANALYSIS_TIMER_ID:
\r
5480 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5481 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5482 AnalysisPeriodicEvent(0);
\r
5484 KillTimer(hwnd, analysisTimerEvent);
\r
5485 analysisTimerEvent = 0;
\r
5488 case DELAYED_TIMER_ID:
\r
5489 KillTimer(hwnd, delayedTimerEvent);
\r
5490 delayedTimerEvent = 0;
\r
5491 delayedTimerCallback();
\r
5496 case WM_USER_Input:
\r
5497 InputEvent(hwnd, message, wParam, lParam);
\r
5500 /* [AS] Also move "attached" child windows */
\r
5501 case WM_WINDOWPOSCHANGING:
\r
5503 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5504 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5506 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5507 /* Window is moving */
\r
5510 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5511 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5512 rcMain.right = wpMain.x + wpMain.width;
\r
5513 rcMain.top = wpMain.y;
\r
5514 rcMain.bottom = wpMain.y + wpMain.height;
\r
5516 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5517 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5518 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5519 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5520 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5521 wpMain.x = lpwp->x;
\r
5522 wpMain.y = lpwp->y;
\r
5527 /* [AS] Snapping */
\r
5528 case WM_ENTERSIZEMOVE:
\r
5529 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5530 if (hwnd == hwndMain) {
\r
5531 doingSizing = TRUE;
\r
5534 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5538 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5539 if (hwnd == hwndMain) {
\r
5540 lastSizing = wParam;
\r
5545 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5546 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5548 case WM_EXITSIZEMOVE:
\r
5549 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5550 if (hwnd == hwndMain) {
\r
5552 doingSizing = FALSE;
\r
5553 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5554 GetClientRect(hwnd, &client);
\r
5555 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5557 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5559 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5562 case WM_DESTROY: /* message: window being destroyed */
\r
5563 PostQuitMessage(0);
\r
5567 if (hwnd == hwndMain) {
\r
5572 default: /* Passes it on if unprocessed */
\r
5573 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5578 /*---------------------------------------------------------------------------*\
\r
5580 * Misc utility routines
\r
5582 \*---------------------------------------------------------------------------*/
\r
5585 * Decent random number generator, at least not as bad as Windows
\r
5586 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5588 unsigned int randstate;
\r
5593 randstate = randstate * 1664525 + 1013904223;
\r
5594 return (int) randstate & 0x7fffffff;
\r
5598 mysrandom(unsigned int seed)
\r
5605 * returns TRUE if user selects a different color, FALSE otherwise
\r
5609 ChangeColor(HWND hwnd, COLORREF *which)
\r
5611 static BOOL firstTime = TRUE;
\r
5612 static DWORD customColors[16];
\r
5614 COLORREF newcolor;
\r
5619 /* Make initial colors in use available as custom colors */
\r
5620 /* Should we put the compiled-in defaults here instead? */
\r
5622 customColors[i++] = lightSquareColor & 0xffffff;
\r
5623 customColors[i++] = darkSquareColor & 0xffffff;
\r
5624 customColors[i++] = whitePieceColor & 0xffffff;
\r
5625 customColors[i++] = blackPieceColor & 0xffffff;
\r
5626 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5627 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5629 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5630 customColors[i++] = textAttribs[ccl].color;
\r
5632 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5633 firstTime = FALSE;
\r
5636 cc.lStructSize = sizeof(cc);
\r
5637 cc.hwndOwner = hwnd;
\r
5638 cc.hInstance = NULL;
\r
5639 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5640 cc.lpCustColors = (LPDWORD) customColors;
\r
5641 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5643 if (!ChooseColor(&cc)) return FALSE;
\r
5645 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5646 if (newcolor == *which) return FALSE;
\r
5647 *which = newcolor;
\r
5651 InitDrawingColors();
\r
5652 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5657 MyLoadSound(MySound *ms)
\r
5663 if (ms->data && ms->flag) free(ms->data);
\r
5666 switch (ms->name[0]) {
\r
5672 /* System sound from Control Panel. Don't preload here. */
\r
5676 if (ms->name[1] == NULLCHAR) {
\r
5677 /* "!" alone = silence */
\r
5680 /* Builtin wave resource. Error if not found. */
\r
5681 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5682 if (h == NULL) break;
\r
5683 ms->data = (void *)LoadResource(hInst, h);
\r
5684 ms->flag = 0; // not maloced, so cannot be freed!
\r
5685 if (h == NULL) break;
\r
5690 /* .wav file. Error if not found. */
\r
5691 f = fopen(ms->name, "rb");
\r
5692 if (f == NULL) break;
\r
5693 if (fstat(fileno(f), &st) < 0) break;
\r
5694 ms->data = malloc(st.st_size);
\r
5696 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5702 char buf[MSG_SIZ];
\r
5703 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5704 DisplayError(buf, GetLastError());
\r
5710 MyPlaySound(MySound *ms)
\r
5712 BOOLEAN ok = FALSE;
\r
5714 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5715 switch (ms->name[0]) {
\r
5717 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5722 /* System sound from Control Panel (deprecated feature).
\r
5723 "$" alone or an unset sound name gets default beep (still in use). */
\r
5724 if (ms->name[1]) {
\r
5725 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5727 if (!ok) ok = MessageBeep(MB_OK);
\r
5730 /* Builtin wave resource, or "!" alone for silence */
\r
5731 if (ms->name[1]) {
\r
5732 if (ms->data == NULL) return FALSE;
\r
5733 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5739 /* .wav file. Error if not found. */
\r
5740 if (ms->data == NULL) return FALSE;
\r
5741 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5744 /* Don't print an error: this can happen innocently if the sound driver
\r
5745 is busy; for instance, if another instance of WinBoard is playing
\r
5746 a sound at about the same time. */
\r
5752 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5755 OPENFILENAME *ofn;
\r
5756 static UINT *number; /* gross that this is static */
\r
5758 switch (message) {
\r
5759 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5760 /* Center the dialog over the application window */
\r
5761 ofn = (OPENFILENAME *) lParam;
\r
5762 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5763 number = (UINT *) ofn->lCustData;
\r
5764 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5768 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5769 Translate(hDlg, 1536);
\r
5770 return FALSE; /* Allow for further processing */
\r
5773 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5774 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5776 return FALSE; /* Allow for further processing */
\r
5782 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5784 static UINT *number;
\r
5785 OPENFILENAME *ofname;
\r
5788 case WM_INITDIALOG:
\r
5789 Translate(hdlg, DLG_IndexNumber);
\r
5790 ofname = (OPENFILENAME *)lParam;
\r
5791 number = (UINT *)(ofname->lCustData);
\r
5794 ofnot = (OFNOTIFY *)lParam;
\r
5795 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5796 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5805 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5806 char *nameFilt, char *dlgTitle, UINT *number,
\r
5807 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5809 OPENFILENAME openFileName;
\r
5810 char buf1[MSG_SIZ];
\r
5813 if (fileName == NULL) fileName = buf1;
\r
5814 if (defName == NULL) {
\r
5815 safeStrCpy(fileName, "*.", 3 );
\r
5816 strcat(fileName, defExt);
\r
5818 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5820 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5821 if (number) *number = 0;
\r
5823 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5824 openFileName.hwndOwner = hwnd;
\r
5825 openFileName.hInstance = (HANDLE) hInst;
\r
5826 openFileName.lpstrFilter = nameFilt;
\r
5827 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5828 openFileName.nMaxCustFilter = 0L;
\r
5829 openFileName.nFilterIndex = 1L;
\r
5830 openFileName.lpstrFile = fileName;
\r
5831 openFileName.nMaxFile = MSG_SIZ;
\r
5832 openFileName.lpstrFileTitle = fileTitle;
\r
5833 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5834 openFileName.lpstrInitialDir = NULL;
\r
5835 openFileName.lpstrTitle = dlgTitle;
\r
5836 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5837 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5838 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5839 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5840 openFileName.nFileOffset = 0;
\r
5841 openFileName.nFileExtension = 0;
\r
5842 openFileName.lpstrDefExt = defExt;
\r
5843 openFileName.lCustData = (LONG) number;
\r
5844 openFileName.lpfnHook = oldDialog ?
\r
5845 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5846 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5848 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5849 GetOpenFileName(&openFileName)) {
\r
5850 /* open the file */
\r
5851 f = fopen(openFileName.lpstrFile, write);
\r
5853 MessageBox(hwnd, _("File open failed"), NULL,
\r
5854 MB_OK|MB_ICONEXCLAMATION);
\r
5858 int err = CommDlgExtendedError();
\r
5859 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5868 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5870 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5873 * Get the first pop-up menu in the menu template. This is the
\r
5874 * menu that TrackPopupMenu displays.
\r
5876 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5877 TranslateOneMenu(10, hmenuTrackPopup);
\r
5879 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5882 * TrackPopup uses screen coordinates, so convert the
\r
5883 * coordinates of the mouse click to screen coordinates.
\r
5885 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5887 /* Draw and track the floating pop-up menu. */
\r
5888 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5889 pt.x, pt.y, 0, hwnd, NULL);
\r
5891 /* Destroy the menu.*/
\r
5892 DestroyMenu(hmenu);
\r
5897 int sizeX, sizeY, newSizeX, newSizeY;
\r
5899 } ResizeEditPlusButtonsClosure;
\r
5902 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5904 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5908 if (hChild == cl->hText) return TRUE;
\r
5909 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5910 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5911 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5912 ScreenToClient(cl->hDlg, &pt);
\r
5913 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5914 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5918 /* Resize a dialog that has a (rich) edit field filling most of
\r
5919 the top, with a row of buttons below */
\r
5921 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5924 int newTextHeight, newTextWidth;
\r
5925 ResizeEditPlusButtonsClosure cl;
\r
5927 /*if (IsIconic(hDlg)) return;*/
\r
5928 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5930 cl.hdwp = BeginDeferWindowPos(8);
\r
5932 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5933 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5934 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5935 if (newTextHeight < 0) {
\r
5936 newSizeY += -newTextHeight;
\r
5937 newTextHeight = 0;
\r
5939 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5940 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5946 cl.newSizeX = newSizeX;
\r
5947 cl.newSizeY = newSizeY;
\r
5948 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5950 EndDeferWindowPos(cl.hdwp);
\r
5953 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5955 RECT rChild, rParent;
\r
5956 int wChild, hChild, wParent, hParent;
\r
5957 int wScreen, hScreen, xNew, yNew;
\r
5960 /* Get the Height and Width of the child window */
\r
5961 GetWindowRect (hwndChild, &rChild);
\r
5962 wChild = rChild.right - rChild.left;
\r
5963 hChild = rChild.bottom - rChild.top;
\r
5965 /* Get the Height and Width of the parent window */
\r
5966 GetWindowRect (hwndParent, &rParent);
\r
5967 wParent = rParent.right - rParent.left;
\r
5968 hParent = rParent.bottom - rParent.top;
\r
5970 /* Get the display limits */
\r
5971 hdc = GetDC (hwndChild);
\r
5972 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5973 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5974 ReleaseDC(hwndChild, hdc);
\r
5976 /* Calculate new X position, then adjust for screen */
\r
5977 xNew = rParent.left + ((wParent - wChild) /2);
\r
5980 } else if ((xNew+wChild) > wScreen) {
\r
5981 xNew = wScreen - wChild;
\r
5984 /* Calculate new Y position, then adjust for screen */
\r
5986 yNew = rParent.top + ((hParent - hChild) /2);
\r
5989 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5994 } else if ((yNew+hChild) > hScreen) {
\r
5995 yNew = hScreen - hChild;
\r
5998 /* Set it, and return */
\r
5999 return SetWindowPos (hwndChild, NULL,
\r
6000 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6003 /* Center one window over another */
\r
6004 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6006 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6009 /*---------------------------------------------------------------------------*\
\r
6011 * Startup Dialog functions
\r
6013 \*---------------------------------------------------------------------------*/
\r
6015 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6017 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6019 while (*cd != NULL) {
\r
6020 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6026 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6028 char buf1[MAX_ARG_LEN];
\r
6031 if (str[0] == '@') {
\r
6032 FILE* f = fopen(str + 1, "r");
\r
6034 DisplayFatalError(str + 1, errno, 2);
\r
6037 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6039 buf1[len] = NULLCHAR;
\r
6043 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6046 char buf[MSG_SIZ];
\r
6047 char *end = strchr(str, '\n');
\r
6048 if (end == NULL) return;
\r
6049 memcpy(buf, str, end - str);
\r
6050 buf[end - str] = NULLCHAR;
\r
6051 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6057 SetStartupDialogEnables(HWND hDlg)
\r
6059 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6060 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6061 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6062 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6063 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6064 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6065 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6066 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6067 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6068 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6069 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6070 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6071 IsDlgButtonChecked(hDlg, OPT_View));
\r
6075 QuoteForFilename(char *filename)
\r
6077 int dquote, space;
\r
6078 dquote = strchr(filename, '"') != NULL;
\r
6079 space = strchr(filename, ' ') != NULL;
\r
6080 if (dquote || space) {
\r
6092 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6094 char buf[MSG_SIZ];
\r
6097 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6098 q = QuoteForFilename(nthcp);
\r
6099 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6100 if (*nthdir != NULLCHAR) {
\r
6101 q = QuoteForFilename(nthdir);
\r
6102 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6104 if (*nthcp == NULLCHAR) {
\r
6105 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6106 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6107 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6108 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6113 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6115 char buf[MSG_SIZ];
\r
6119 switch (message) {
\r
6120 case WM_INITDIALOG:
\r
6121 /* Center the dialog */
\r
6122 CenterWindow (hDlg, GetDesktopWindow());
\r
6123 Translate(hDlg, DLG_Startup);
\r
6124 /* Initialize the dialog items */
\r
6125 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6126 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6127 firstChessProgramNames);
\r
6128 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6129 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6130 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6131 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6132 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6133 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6134 if (*appData.icsHelper != NULLCHAR) {
\r
6135 char *q = QuoteForFilename(appData.icsHelper);
\r
6136 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6138 if (*appData.icsHost == NULLCHAR) {
\r
6139 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6140 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6141 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6142 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6143 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6146 if (appData.icsActive) {
\r
6147 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6149 else if (appData.noChessProgram) {
\r
6150 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6153 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6156 SetStartupDialogEnables(hDlg);
\r
6160 switch (LOWORD(wParam)) {
\r
6162 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6163 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6164 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6166 ParseArgs(StringGet, &p);
\r
6167 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6168 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6170 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6171 ParseArgs(StringGet, &p);
\r
6172 SwapEngines(singleList); // ... and then make it 'second'
\r
6173 appData.noChessProgram = FALSE;
\r
6174 appData.icsActive = FALSE;
\r
6175 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6176 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6177 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6179 ParseArgs(StringGet, &p);
\r
6180 if (appData.zippyPlay) {
\r
6181 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6182 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6184 ParseArgs(StringGet, &p);
\r
6186 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6187 appData.noChessProgram = TRUE;
\r
6188 appData.icsActive = FALSE;
\r
6190 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6191 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6194 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6195 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6197 ParseArgs(StringGet, &p);
\r
6199 EndDialog(hDlg, TRUE);
\r
6206 case IDM_HELPCONTENTS:
\r
6207 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6208 MessageBox (GetFocus(),
\r
6209 _("Unable to activate help"),
\r
6210 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6215 SetStartupDialogEnables(hDlg);
\r
6223 /*---------------------------------------------------------------------------*\
\r
6225 * About box dialog functions
\r
6227 \*---------------------------------------------------------------------------*/
\r
6229 /* Process messages for "About" dialog box */
\r
6231 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6233 switch (message) {
\r
6234 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6235 /* Center the dialog over the application window */
\r
6236 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6237 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6238 Translate(hDlg, ABOUTBOX);
\r
6242 case WM_COMMAND: /* message: received a command */
\r
6243 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6244 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6245 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6253 /*---------------------------------------------------------------------------*\
\r
6255 * Comment Dialog functions
\r
6257 \*---------------------------------------------------------------------------*/
\r
6260 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6262 static HANDLE hwndText = NULL;
\r
6263 int len, newSizeX, newSizeY, flags;
\r
6264 static int sizeX, sizeY;
\r
6269 switch (message) {
\r
6270 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6271 /* Initialize the dialog items */
\r
6272 Translate(hDlg, DLG_EditComment);
\r
6273 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6274 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6275 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6276 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6277 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6278 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6279 SetWindowText(hDlg, commentTitle);
\r
6280 if (editComment) {
\r
6281 SetFocus(hwndText);
\r
6283 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6285 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6286 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6287 MAKELPARAM(FALSE, 0));
\r
6288 /* Size and position the dialog */
\r
6289 if (!commentDialog) {
\r
6290 commentDialog = hDlg;
\r
6291 flags = SWP_NOZORDER;
\r
6292 GetClientRect(hDlg, &rect);
\r
6293 sizeX = rect.right;
\r
6294 sizeY = rect.bottom;
\r
6295 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6296 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6297 WINDOWPLACEMENT wp;
\r
6298 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6299 wp.length = sizeof(WINDOWPLACEMENT);
\r
6301 wp.showCmd = SW_SHOW;
\r
6302 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6303 wp.rcNormalPosition.left = wpComment.x;
\r
6304 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6305 wp.rcNormalPosition.top = wpComment.y;
\r
6306 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6307 SetWindowPlacement(hDlg, &wp);
\r
6309 GetClientRect(hDlg, &rect);
\r
6310 newSizeX = rect.right;
\r
6311 newSizeY = rect.bottom;
\r
6312 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6313 newSizeX, newSizeY);
\r
6318 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6321 case WM_COMMAND: /* message: received a command */
\r
6322 switch (LOWORD(wParam)) {
\r
6324 if (editComment) {
\r
6326 /* Read changed options from the dialog box */
\r
6327 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6328 len = GetWindowTextLength(hwndText);
\r
6329 str = (char *) malloc(len + 1);
\r
6330 GetWindowText(hwndText, str, len + 1);
\r
6339 ReplaceComment(commentIndex, str);
\r
6346 case OPT_CancelComment:
\r
6350 case OPT_ClearComment:
\r
6351 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6354 case OPT_EditComment:
\r
6355 EditCommentEvent();
\r
6363 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6364 if( wParam == OPT_CommentText ) {
\r
6365 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6367 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6368 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6372 pt.x = LOWORD( lpMF->lParam );
\r
6373 pt.y = HIWORD( lpMF->lParam );
\r
6375 if(lpMF->msg == WM_CHAR) {
\r
6377 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6378 index = sel.cpMin;
\r
6380 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6382 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6383 len = GetWindowTextLength(hwndText);
\r
6384 str = (char *) malloc(len + 1);
\r
6385 GetWindowText(hwndText, str, len + 1);
\r
6386 ReplaceComment(commentIndex, str);
\r
6387 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6388 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6391 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6392 lpMF->msg = WM_USER;
\r
6400 newSizeX = LOWORD(lParam);
\r
6401 newSizeY = HIWORD(lParam);
\r
6402 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6407 case WM_GETMINMAXINFO:
\r
6408 /* Prevent resizing window too small */
\r
6409 mmi = (MINMAXINFO *) lParam;
\r
6410 mmi->ptMinTrackSize.x = 100;
\r
6411 mmi->ptMinTrackSize.y = 100;
\r
6418 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6423 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6425 if (str == NULL) str = "";
\r
6426 p = (char *) malloc(2 * strlen(str) + 2);
\r
6429 if (*str == '\n') *q++ = '\r';
\r
6433 if (commentText != NULL) free(commentText);
\r
6435 commentIndex = index;
\r
6436 commentTitle = title;
\r
6438 editComment = edit;
\r
6440 if (commentDialog) {
\r
6441 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6442 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6444 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6445 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6446 hwndMain, (DLGPROC)lpProc);
\r
6447 FreeProcInstance(lpProc);
\r
6453 /*---------------------------------------------------------------------------*\
\r
6455 * Type-in move dialog functions
\r
6457 \*---------------------------------------------------------------------------*/
\r
6460 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6462 char move[MSG_SIZ];
\r
6465 switch (message) {
\r
6466 case WM_INITDIALOG:
\r
6467 move[0] = (char) lParam;
\r
6468 move[1] = NULLCHAR;
\r
6469 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6470 Translate(hDlg, DLG_TypeInMove);
\r
6471 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6472 SetWindowText(hInput, move);
\r
6474 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6478 switch (LOWORD(wParam)) {
\r
6481 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6482 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6483 TypeInDoneEvent(move);
\r
6484 EndDialog(hDlg, TRUE);
\r
6487 EndDialog(hDlg, FALSE);
\r
6498 PopUpMoveDialog(char firstchar)
\r
6502 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6503 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6504 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6505 FreeProcInstance(lpProc);
\r
6508 /*---------------------------------------------------------------------------*\
\r
6510 * Type-in name dialog functions
\r
6512 \*---------------------------------------------------------------------------*/
\r
6515 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6517 char move[MSG_SIZ];
\r
6520 switch (message) {
\r
6521 case WM_INITDIALOG:
\r
6522 move[0] = (char) lParam;
\r
6523 move[1] = NULLCHAR;
\r
6524 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6525 Translate(hDlg, DLG_TypeInName);
\r
6526 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6527 SetWindowText(hInput, move);
\r
6529 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6533 switch (LOWORD(wParam)) {
\r
6535 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6536 appData.userName = strdup(move);
\r
6539 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6540 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6541 DisplayTitle(move);
\r
6545 EndDialog(hDlg, TRUE);
\r
6548 EndDialog(hDlg, FALSE);
\r
6559 PopUpNameDialog(char firstchar)
\r
6563 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6564 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6565 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6566 FreeProcInstance(lpProc);
\r
6569 /*---------------------------------------------------------------------------*\
\r
6573 \*---------------------------------------------------------------------------*/
\r
6575 /* Nonmodal error box */
\r
6576 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6577 WPARAM wParam, LPARAM lParam);
\r
6580 ErrorPopUp(char *title, char *content)
\r
6584 BOOLEAN modal = hwndMain == NULL;
\r
6602 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6603 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6606 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6608 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6609 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6610 hwndMain, (DLGPROC)lpProc);
\r
6611 FreeProcInstance(lpProc);
\r
6618 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6619 if (errorDialog == NULL) return;
\r
6620 DestroyWindow(errorDialog);
\r
6621 errorDialog = NULL;
\r
6622 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6626 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6631 switch (message) {
\r
6632 case WM_INITDIALOG:
\r
6633 GetWindowRect(hDlg, &rChild);
\r
6636 SetWindowPos(hDlg, NULL, rChild.left,
\r
6637 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6638 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6642 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6643 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6644 and it doesn't work when you resize the dialog.
\r
6645 For now, just give it a default position.
\r
6647 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6648 Translate(hDlg, DLG_Error);
\r
6650 errorDialog = hDlg;
\r
6651 SetWindowText(hDlg, errorTitle);
\r
6652 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6653 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6657 switch (LOWORD(wParam)) {
\r
6660 if (errorDialog == hDlg) errorDialog = NULL;
\r
6661 DestroyWindow(hDlg);
\r
6673 HWND gothicDialog = NULL;
\r
6676 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6680 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6682 switch (message) {
\r
6683 case WM_INITDIALOG:
\r
6684 GetWindowRect(hDlg, &rChild);
\r
6686 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6690 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6691 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6692 and it doesn't work when you resize the dialog.
\r
6693 For now, just give it a default position.
\r
6695 gothicDialog = hDlg;
\r
6696 SetWindowText(hDlg, errorTitle);
\r
6697 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6698 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6702 switch (LOWORD(wParam)) {
\r
6705 if (errorDialog == hDlg) errorDialog = NULL;
\r
6706 DestroyWindow(hDlg);
\r
6718 GothicPopUp(char *title, VariantClass variant)
\r
6721 static char *lastTitle;
\r
6723 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6724 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6726 if(lastTitle != title && gothicDialog != NULL) {
\r
6727 DestroyWindow(gothicDialog);
\r
6728 gothicDialog = NULL;
\r
6730 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6731 title = lastTitle;
\r
6732 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6733 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6734 hwndMain, (DLGPROC)lpProc);
\r
6735 FreeProcInstance(lpProc);
\r
6740 /*---------------------------------------------------------------------------*\
\r
6742 * Ics Interaction console functions
\r
6744 \*---------------------------------------------------------------------------*/
\r
6746 #define HISTORY_SIZE 64
\r
6747 static char *history[HISTORY_SIZE];
\r
6748 int histIn = 0, histP = 0;
\r
6751 SaveInHistory(char *cmd)
\r
6753 if (history[histIn] != NULL) {
\r
6754 free(history[histIn]);
\r
6755 history[histIn] = NULL;
\r
6757 if (*cmd == NULLCHAR) return;
\r
6758 history[histIn] = StrSave(cmd);
\r
6759 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6760 if (history[histIn] != NULL) {
\r
6761 free(history[histIn]);
\r
6762 history[histIn] = NULL;
\r
6768 PrevInHistory(char *cmd)
\r
6771 if (histP == histIn) {
\r
6772 if (history[histIn] != NULL) free(history[histIn]);
\r
6773 history[histIn] = StrSave(cmd);
\r
6775 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6776 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6778 return history[histP];
\r
6784 if (histP == histIn) return NULL;
\r
6785 histP = (histP + 1) % HISTORY_SIZE;
\r
6786 return history[histP];
\r
6790 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6794 hmenu = LoadMenu(hInst, "TextMenu");
\r
6795 h = GetSubMenu(hmenu, 0);
\r
6797 if (strcmp(e->item, "-") == 0) {
\r
6798 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6799 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6800 int flags = MF_STRING, j = 0;
\r
6801 if (e->item[0] == '|') {
\r
6802 flags |= MF_MENUBARBREAK;
\r
6805 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6806 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6814 WNDPROC consoleTextWindowProc;
\r
6817 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6819 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6820 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6824 SetWindowText(hInput, command);
\r
6826 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6828 sel.cpMin = 999999;
\r
6829 sel.cpMax = 999999;
\r
6830 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6835 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6836 if (sel.cpMin == sel.cpMax) {
\r
6837 /* Expand to surrounding word */
\r
6840 tr.chrg.cpMax = sel.cpMin;
\r
6841 tr.chrg.cpMin = --sel.cpMin;
\r
6842 if (sel.cpMin < 0) break;
\r
6843 tr.lpstrText = name;
\r
6844 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6845 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6849 tr.chrg.cpMin = sel.cpMax;
\r
6850 tr.chrg.cpMax = ++sel.cpMax;
\r
6851 tr.lpstrText = name;
\r
6852 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6853 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6856 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6857 MessageBeep(MB_ICONEXCLAMATION);
\r
6861 tr.lpstrText = name;
\r
6862 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6864 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6865 MessageBeep(MB_ICONEXCLAMATION);
\r
6868 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6871 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6872 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6873 SetWindowText(hInput, buf);
\r
6874 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6876 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6877 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6878 SetWindowText(hInput, buf);
\r
6879 sel.cpMin = 999999;
\r
6880 sel.cpMax = 999999;
\r
6881 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6887 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6892 switch (message) {
\r
6894 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6895 if(wParam=='R') return 0;
\r
6898 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6901 sel.cpMin = 999999;
\r
6902 sel.cpMax = 999999;
\r
6903 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6904 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6909 if(wParam != '\022') {
\r
6910 if (wParam == '\t') {
\r
6911 if (GetKeyState(VK_SHIFT) < 0) {
\r
6913 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6914 if (buttonDesc[0].hwnd) {
\r
6915 SetFocus(buttonDesc[0].hwnd);
\r
6917 SetFocus(hwndMain);
\r
6921 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6924 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6925 JAWS_DELETE( SetFocus(hInput); )
\r
6926 SendMessage(hInput, message, wParam, lParam);
\r
6929 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6931 case WM_RBUTTONDOWN:
\r
6932 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6933 /* Move selection here if it was empty */
\r
6935 pt.x = LOWORD(lParam);
\r
6936 pt.y = HIWORD(lParam);
\r
6937 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6938 if (sel.cpMin == sel.cpMax) {
\r
6939 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6940 sel.cpMax = sel.cpMin;
\r
6941 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6943 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6944 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6946 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6947 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6948 if (sel.cpMin == sel.cpMax) {
\r
6949 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6950 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6952 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6953 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6955 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6956 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6957 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6958 MenuPopup(hwnd, pt, hmenu, -1);
\r
6962 case WM_RBUTTONUP:
\r
6963 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6964 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6965 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6969 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6971 return SendMessage(hInput, message, wParam, lParam);
\r
6972 case WM_MBUTTONDOWN:
\r
6973 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6975 switch (LOWORD(wParam)) {
\r
6976 case IDM_QuickPaste:
\r
6978 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6979 if (sel.cpMin == sel.cpMax) {
\r
6980 MessageBeep(MB_ICONEXCLAMATION);
\r
6983 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6984 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6985 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6990 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6993 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6996 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7000 int i = LOWORD(wParam) - IDM_CommandX;
\r
7001 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7002 icsTextMenuEntry[i].command != NULL) {
\r
7003 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7004 icsTextMenuEntry[i].getname,
\r
7005 icsTextMenuEntry[i].immediate);
\r
7013 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7016 WNDPROC consoleInputWindowProc;
\r
7019 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7021 char buf[MSG_SIZ];
\r
7023 static BOOL sendNextChar = FALSE;
\r
7024 static BOOL quoteNextChar = FALSE;
\r
7025 InputSource *is = consoleInputSource;
\r
7029 switch (message) {
\r
7031 if (!appData.localLineEditing || sendNextChar) {
\r
7032 is->buf[0] = (CHAR) wParam;
\r
7034 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7035 sendNextChar = FALSE;
\r
7038 if (quoteNextChar) {
\r
7039 buf[0] = (char) wParam;
\r
7040 buf[1] = NULLCHAR;
\r
7041 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7042 quoteNextChar = FALSE;
\r
7046 case '\r': /* Enter key */
\r
7047 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7048 if (consoleEcho) SaveInHistory(is->buf);
\r
7049 is->buf[is->count++] = '\n';
\r
7050 is->buf[is->count] = NULLCHAR;
\r
7051 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7052 if (consoleEcho) {
\r
7053 ConsoleOutput(is->buf, is->count, TRUE);
\r
7054 } else if (appData.localLineEditing) {
\r
7055 ConsoleOutput("\n", 1, TRUE);
\r
7058 case '\033': /* Escape key */
\r
7059 SetWindowText(hwnd, "");
\r
7060 cf.cbSize = sizeof(CHARFORMAT);
\r
7061 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7062 if (consoleEcho) {
\r
7063 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7065 cf.crTextColor = COLOR_ECHOOFF;
\r
7067 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7068 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7070 case '\t': /* Tab key */
\r
7071 if (GetKeyState(VK_SHIFT) < 0) {
\r
7073 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7076 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7077 if (buttonDesc[0].hwnd) {
\r
7078 SetFocus(buttonDesc[0].hwnd);
\r
7080 SetFocus(hwndMain);
\r
7084 case '\023': /* Ctrl+S */
\r
7085 sendNextChar = TRUE;
\r
7087 case '\021': /* Ctrl+Q */
\r
7088 quoteNextChar = TRUE;
\r
7098 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7099 p = PrevInHistory(buf);
\r
7101 SetWindowText(hwnd, p);
\r
7102 sel.cpMin = 999999;
\r
7103 sel.cpMax = 999999;
\r
7104 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7109 p = NextInHistory();
\r
7111 SetWindowText(hwnd, p);
\r
7112 sel.cpMin = 999999;
\r
7113 sel.cpMax = 999999;
\r
7114 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7120 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7124 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7128 case WM_MBUTTONDOWN:
\r
7129 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7130 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7132 case WM_RBUTTONUP:
\r
7133 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7134 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7135 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7139 hmenu = LoadMenu(hInst, "InputMenu");
\r
7140 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7141 if (sel.cpMin == sel.cpMax) {
\r
7142 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7143 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7145 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7146 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7148 pt.x = LOWORD(lParam);
\r
7149 pt.y = HIWORD(lParam);
\r
7150 MenuPopup(hwnd, pt, hmenu, -1);
\r
7154 switch (LOWORD(wParam)) {
\r
7156 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7158 case IDM_SelectAll:
\r
7160 sel.cpMax = -1; /*999999?*/
\r
7161 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7164 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7167 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7170 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7175 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7178 #define CO_MAX 100000
\r
7179 #define CO_TRIM 1000
\r
7182 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7184 static SnapData sd;
\r
7185 HWND hText, hInput;
\r
7187 static int sizeX, sizeY;
\r
7188 int newSizeX, newSizeY;
\r
7192 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7193 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7195 switch (message) {
\r
7197 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7199 ENLINK *pLink = (ENLINK*)lParam;
\r
7200 if (pLink->msg == WM_LBUTTONUP)
\r
7204 tr.chrg = pLink->chrg;
\r
7205 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7206 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7207 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7208 free(tr.lpstrText);
\r
7212 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7213 hwndConsole = hDlg;
\r
7215 consoleTextWindowProc = (WNDPROC)
\r
7216 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7217 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7218 consoleInputWindowProc = (WNDPROC)
\r
7219 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7220 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7221 Colorize(ColorNormal, TRUE);
\r
7222 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7223 ChangedConsoleFont();
\r
7224 GetClientRect(hDlg, &rect);
\r
7225 sizeX = rect.right;
\r
7226 sizeY = rect.bottom;
\r
7227 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7228 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7229 WINDOWPLACEMENT wp;
\r
7230 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7231 wp.length = sizeof(WINDOWPLACEMENT);
\r
7233 wp.showCmd = SW_SHOW;
\r
7234 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7235 wp.rcNormalPosition.left = wpConsole.x;
\r
7236 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7237 wp.rcNormalPosition.top = wpConsole.y;
\r
7238 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7239 SetWindowPlacement(hDlg, &wp);
\r
7242 // [HGM] Chessknight's change 2004-07-13
\r
7243 else { /* Determine Defaults */
\r
7244 WINDOWPLACEMENT wp;
\r
7245 wpConsole.x = wpMain.width + 1;
\r
7246 wpConsole.y = wpMain.y;
\r
7247 wpConsole.width = screenWidth - wpMain.width;
\r
7248 wpConsole.height = wpMain.height;
\r
7249 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7250 wp.length = sizeof(WINDOWPLACEMENT);
\r
7252 wp.showCmd = SW_SHOW;
\r
7253 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7254 wp.rcNormalPosition.left = wpConsole.x;
\r
7255 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7256 wp.rcNormalPosition.top = wpConsole.y;
\r
7257 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7258 SetWindowPlacement(hDlg, &wp);
\r
7261 // Allow hText to highlight URLs and send notifications on them
\r
7262 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7263 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7264 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7265 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7279 if (IsIconic(hDlg)) break;
\r
7280 newSizeX = LOWORD(lParam);
\r
7281 newSizeY = HIWORD(lParam);
\r
7282 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7283 RECT rectText, rectInput;
\r
7285 int newTextHeight, newTextWidth;
\r
7286 GetWindowRect(hText, &rectText);
\r
7287 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7288 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7289 if (newTextHeight < 0) {
\r
7290 newSizeY += -newTextHeight;
\r
7291 newTextHeight = 0;
\r
7293 SetWindowPos(hText, NULL, 0, 0,
\r
7294 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7295 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7296 pt.x = rectInput.left;
\r
7297 pt.y = rectInput.top + newSizeY - sizeY;
\r
7298 ScreenToClient(hDlg, &pt);
\r
7299 SetWindowPos(hInput, NULL,
\r
7300 pt.x, pt.y, /* needs client coords */
\r
7301 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7302 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7308 case WM_GETMINMAXINFO:
\r
7309 /* Prevent resizing window too small */
\r
7310 mmi = (MINMAXINFO *) lParam;
\r
7311 mmi->ptMinTrackSize.x = 100;
\r
7312 mmi->ptMinTrackSize.y = 100;
\r
7315 /* [AS] Snapping */
\r
7316 case WM_ENTERSIZEMOVE:
\r
7317 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7320 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7323 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7325 case WM_EXITSIZEMOVE:
\r
7326 UpdateICSWidth(hText);
\r
7327 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7330 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7338 if (hwndConsole) return;
\r
7339 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7340 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7345 ConsoleOutput(char* data, int length, int forceVisible)
\r
7350 char buf[CO_MAX+1];
\r
7353 static int delayLF = 0;
\r
7354 CHARRANGE savesel, sel;
\r
7356 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7364 while (length--) {
\r
7372 } else if (*p == '\007') {
\r
7373 MyPlaySound(&sounds[(int)SoundBell]);
\r
7380 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7381 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7382 /* Save current selection */
\r
7383 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7384 exlen = GetWindowTextLength(hText);
\r
7385 /* Find out whether current end of text is visible */
\r
7386 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7387 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7388 /* Trim existing text if it's too long */
\r
7389 if (exlen + (q - buf) > CO_MAX) {
\r
7390 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7393 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7394 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7396 savesel.cpMin -= trim;
\r
7397 savesel.cpMax -= trim;
\r
7398 if (exlen < 0) exlen = 0;
\r
7399 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7400 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7402 /* Append the new text */
\r
7403 sel.cpMin = exlen;
\r
7404 sel.cpMax = exlen;
\r
7405 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7406 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7407 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7408 if (forceVisible || exlen == 0 ||
\r
7409 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7410 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7411 /* Scroll to make new end of text visible if old end of text
\r
7412 was visible or new text is an echo of user typein */
\r
7413 sel.cpMin = 9999999;
\r
7414 sel.cpMax = 9999999;
\r
7415 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7416 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7417 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7418 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7420 if (savesel.cpMax == exlen || forceVisible) {
\r
7421 /* Move insert point to new end of text if it was at the old
\r
7422 end of text or if the new text is an echo of user typein */
\r
7423 sel.cpMin = 9999999;
\r
7424 sel.cpMax = 9999999;
\r
7425 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7427 /* Restore previous selection */
\r
7428 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7430 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7437 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7441 COLORREF oldFg, oldBg;
\r
7446 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7448 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7449 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7450 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7453 rect.right = x + squareSize;
\r
7455 rect.bottom = y + squareSize;
\r
7458 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7459 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7460 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7461 &rect, str, strlen(str), NULL);
\r
7463 (void) SetTextColor(hdc, oldFg);
\r
7464 (void) SetBkColor(hdc, oldBg);
\r
7465 (void) SelectObject(hdc, oldFont);
\r
7469 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7470 RECT *rect, char *color, char *flagFell)
\r
7474 COLORREF oldFg, oldBg;
\r
7477 if (appData.clockMode) {
\r
7479 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7481 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7488 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7489 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7491 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7492 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7494 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7498 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7499 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7500 rect, str, strlen(str), NULL);
\r
7501 if(logoHeight > 0 && appData.clockMode) {
\r
7503 str += strlen(color)+2;
\r
7504 r.top = rect->top + logoHeight/2;
\r
7505 r.left = rect->left;
\r
7506 r.right = rect->right;
\r
7507 r.bottom = rect->bottom;
\r
7508 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7509 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7510 &r, str, strlen(str), NULL);
\r
7512 (void) SetTextColor(hdc, oldFg);
\r
7513 (void) SetBkColor(hdc, oldBg);
\r
7514 (void) SelectObject(hdc, oldFont);
\r
7519 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7525 if( count <= 0 ) {
\r
7526 if (appData.debugMode) {
\r
7527 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7530 return ERROR_INVALID_USER_BUFFER;
\r
7533 ResetEvent(ovl->hEvent);
\r
7534 ovl->Offset = ovl->OffsetHigh = 0;
\r
7535 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7539 err = GetLastError();
\r
7540 if (err == ERROR_IO_PENDING) {
\r
7541 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7545 err = GetLastError();
\r
7552 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7557 ResetEvent(ovl->hEvent);
\r
7558 ovl->Offset = ovl->OffsetHigh = 0;
\r
7559 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7563 err = GetLastError();
\r
7564 if (err == ERROR_IO_PENDING) {
\r
7565 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7569 err = GetLastError();
\r
7575 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7576 void CheckForInputBufferFull( InputSource * is )
\r
7578 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7579 /* Look for end of line */
\r
7580 char * p = is->buf;
\r
7582 while( p < is->next && *p != '\n' ) {
\r
7586 if( p >= is->next ) {
\r
7587 if (appData.debugMode) {
\r
7588 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7591 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7592 is->count = (DWORD) -1;
\r
7593 is->next = is->buf;
\r
7599 InputThread(LPVOID arg)
\r
7604 is = (InputSource *) arg;
\r
7605 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7606 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7607 while (is->hThread != NULL) {
\r
7608 is->error = DoReadFile(is->hFile, is->next,
\r
7609 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7610 &is->count, &ovl);
\r
7611 if (is->error == NO_ERROR) {
\r
7612 is->next += is->count;
\r
7614 if (is->error == ERROR_BROKEN_PIPE) {
\r
7615 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7618 is->count = (DWORD) -1;
\r
7619 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7624 CheckForInputBufferFull( is );
\r
7626 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7628 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7630 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7633 CloseHandle(ovl.hEvent);
\r
7634 CloseHandle(is->hFile);
\r
7636 if (appData.debugMode) {
\r
7637 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7644 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7646 NonOvlInputThread(LPVOID arg)
\r
7653 is = (InputSource *) arg;
\r
7654 while (is->hThread != NULL) {
\r
7655 is->error = ReadFile(is->hFile, is->next,
\r
7656 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7657 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7658 if (is->error == NO_ERROR) {
\r
7659 /* Change CRLF to LF */
\r
7660 if (is->next > is->buf) {
\r
7662 i = is->count + 1;
\r
7670 if (prev == '\r' && *p == '\n') {
\r
7682 if (is->error == ERROR_BROKEN_PIPE) {
\r
7683 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7686 is->count = (DWORD) -1;
\r
7690 CheckForInputBufferFull( is );
\r
7692 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7694 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7696 if (is->count < 0) break; /* Quit on error */
\r
7698 CloseHandle(is->hFile);
\r
7703 SocketInputThread(LPVOID arg)
\r
7707 is = (InputSource *) arg;
\r
7708 while (is->hThread != NULL) {
\r
7709 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7710 if ((int)is->count == SOCKET_ERROR) {
\r
7711 is->count = (DWORD) -1;
\r
7712 is->error = WSAGetLastError();
\r
7714 is->error = NO_ERROR;
\r
7715 is->next += is->count;
\r
7716 if (is->count == 0 && is->second == is) {
\r
7717 /* End of file on stderr; quit with no message */
\r
7721 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7723 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7725 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7731 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7735 is = (InputSource *) lParam;
\r
7736 if (is->lineByLine) {
\r
7737 /* Feed in lines one by one */
\r
7738 char *p = is->buf;
\r
7740 while (q < is->next) {
\r
7741 if (*q++ == '\n') {
\r
7742 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7747 /* Move any partial line to the start of the buffer */
\r
7749 while (p < is->next) {
\r
7754 if (is->error != NO_ERROR || is->count == 0) {
\r
7755 /* Notify backend of the error. Note: If there was a partial
\r
7756 line at the end, it is not flushed through. */
\r
7757 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7760 /* Feed in the whole chunk of input at once */
\r
7761 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7762 is->next = is->buf;
\r
7766 /*---------------------------------------------------------------------------*\
\r
7768 * Menu enables. Used when setting various modes.
\r
7770 \*---------------------------------------------------------------------------*/
\r
7778 GreyRevert(Boolean grey)
\r
7779 { // [HGM] vari: for retracting variations in local mode
\r
7780 HMENU hmenu = GetMenu(hwndMain);
\r
7781 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7782 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7786 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7788 while (enab->item > 0) {
\r
7789 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7794 Enables gnuEnables[] = {
\r
7795 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7809 // Needed to switch from ncp to GNU mode on Engine Load
\r
7810 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7811 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7812 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7813 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7814 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7816 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7817 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7818 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7819 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7820 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7821 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7822 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7823 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7827 Enables icsEnables[] = {
\r
7828 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7836 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7843 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7851 Enables zippyEnables[] = {
\r
7852 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7853 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7854 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7860 Enables ncpEnables[] = {
\r
7861 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7871 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7886 Enables trainingOnEnables[] = {
\r
7887 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7899 Enables trainingOffEnables[] = {
\r
7900 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7912 /* These modify either ncpEnables or gnuEnables */
\r
7913 Enables cmailEnables[] = {
\r
7914 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7917 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7924 Enables machineThinkingEnables[] = {
\r
7925 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7927 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7929 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7938 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7944 Enables userThinkingEnables[] = {
\r
7945 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7946 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7948 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7949 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7950 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7951 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7952 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7953 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7954 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7955 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7956 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7957 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7958 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7959 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7960 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7964 /*---------------------------------------------------------------------------*\
\r
7966 * Front-end interface functions exported by XBoard.
\r
7967 * Functions appear in same order as prototypes in frontend.h.
\r
7969 \*---------------------------------------------------------------------------*/
\r
7971 CheckMark(UINT item, int state)
\r
7973 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7979 static UINT prevChecked = 0;
\r
7980 static int prevPausing = 0;
\r
7983 if (pausing != prevPausing) {
\r
7984 prevPausing = pausing;
\r
7985 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7986 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7987 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7990 switch (gameMode) {
\r
7991 case BeginningOfGame:
\r
7992 if (appData.icsActive)
\r
7993 nowChecked = IDM_IcsClient;
\r
7994 else if (appData.noChessProgram)
\r
7995 nowChecked = IDM_EditGame;
\r
7997 nowChecked = IDM_MachineBlack;
\r
7999 case MachinePlaysBlack:
\r
8000 nowChecked = IDM_MachineBlack;
\r
8002 case MachinePlaysWhite:
\r
8003 nowChecked = IDM_MachineWhite;
\r
8005 case TwoMachinesPlay:
\r
8006 nowChecked = IDM_TwoMachines;
\r
8009 nowChecked = IDM_AnalysisMode;
\r
8012 nowChecked = IDM_AnalyzeFile;
\r
8015 nowChecked = IDM_EditGame;
\r
8017 case PlayFromGameFile:
\r
8018 nowChecked = IDM_LoadGame;
\r
8020 case EditPosition:
\r
8021 nowChecked = IDM_EditPosition;
\r
8024 nowChecked = IDM_Training;
\r
8026 case IcsPlayingWhite:
\r
8027 case IcsPlayingBlack:
\r
8028 case IcsObserving:
\r
8030 nowChecked = IDM_IcsClient;
\r
8037 CheckMark(prevChecked, MF_UNCHECKED);
\r
8038 CheckMark(nowChecked, MF_CHECKED);
\r
8039 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8041 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8042 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8043 MF_BYCOMMAND|MF_ENABLED);
\r
8045 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8046 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8049 prevChecked = nowChecked;
\r
8051 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8052 if (appData.icsActive) {
\r
8053 if (appData.icsEngineAnalyze) {
\r
8054 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8056 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8059 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8065 HMENU hmenu = GetMenu(hwndMain);
\r
8066 SetMenuEnables(hmenu, icsEnables);
\r
8067 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8068 MF_BYCOMMAND|MF_ENABLED);
\r
8070 if (appData.zippyPlay) {
\r
8071 SetMenuEnables(hmenu, zippyEnables);
\r
8072 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8073 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8074 MF_BYCOMMAND|MF_ENABLED);
\r
8082 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8088 HMENU hmenu = GetMenu(hwndMain);
\r
8089 SetMenuEnables(hmenu, ncpEnables);
\r
8090 DrawMenuBar(hwndMain);
\r
8096 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8100 SetTrainingModeOn()
\r
8103 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8104 for (i = 0; i < N_BUTTONS; i++) {
\r
8105 if (buttonDesc[i].hwnd != NULL)
\r
8106 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8111 VOID SetTrainingModeOff()
\r
8114 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8115 for (i = 0; i < N_BUTTONS; i++) {
\r
8116 if (buttonDesc[i].hwnd != NULL)
\r
8117 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8123 SetUserThinkingEnables()
\r
8125 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8129 SetMachineThinkingEnables()
\r
8131 HMENU hMenu = GetMenu(hwndMain);
\r
8132 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8134 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8136 if (gameMode == MachinePlaysBlack) {
\r
8137 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8138 } else if (gameMode == MachinePlaysWhite) {
\r
8139 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8140 } else if (gameMode == TwoMachinesPlay) {
\r
8141 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8147 DisplayTitle(char *str)
\r
8149 char title[MSG_SIZ], *host;
\r
8150 if (str[0] != NULLCHAR) {
\r
8151 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8152 } else if (appData.icsActive) {
\r
8153 if (appData.icsCommPort[0] != NULLCHAR)
\r
8156 host = appData.icsHost;
\r
8157 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8158 } else if (appData.noChessProgram) {
\r
8159 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8161 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8162 strcat(title, ": ");
\r
8163 strcat(title, first.tidy);
\r
8165 SetWindowText(hwndMain, title);
\r
8170 DisplayMessage(char *str1, char *str2)
\r
8174 int remain = MESSAGE_TEXT_MAX - 1;
\r
8177 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8178 messageText[0] = NULLCHAR;
\r
8180 len = strlen(str1);
\r
8181 if (len > remain) len = remain;
\r
8182 strncpy(messageText, str1, len);
\r
8183 messageText[len] = NULLCHAR;
\r
8186 if (*str2 && remain >= 2) {
\r
8188 strcat(messageText, " ");
\r
8191 len = strlen(str2);
\r
8192 if (len > remain) len = remain;
\r
8193 strncat(messageText, str2, len);
\r
8195 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8196 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8198 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8202 hdc = GetDC(hwndMain);
\r
8203 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8204 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8205 &messageRect, messageText, strlen(messageText), NULL);
\r
8206 (void) SelectObject(hdc, oldFont);
\r
8207 (void) ReleaseDC(hwndMain, hdc);
\r
8211 DisplayError(char *str, int error)
\r
8213 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8217 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8219 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8220 NULL, error, LANG_NEUTRAL,
\r
8221 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8223 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8225 ErrorMap *em = errmap;
\r
8226 while (em->err != 0 && em->err != error) em++;
\r
8227 if (em->err != 0) {
\r
8228 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8230 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8235 ErrorPopUp(_("Error"), buf);
\r
8240 DisplayMoveError(char *str)
\r
8242 fromX = fromY = -1;
\r
8243 ClearHighlights();
\r
8244 DrawPosition(FALSE, NULL);
\r
8245 if (appData.popupMoveErrors) {
\r
8246 ErrorPopUp(_("Error"), str);
\r
8248 DisplayMessage(str, "");
\r
8249 moveErrorMessageUp = TRUE;
\r
8254 DisplayFatalError(char *str, int error, int exitStatus)
\r
8256 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8258 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8261 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8262 NULL, error, LANG_NEUTRAL,
\r
8263 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8265 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8267 ErrorMap *em = errmap;
\r
8268 while (em->err != 0 && em->err != error) em++;
\r
8269 if (em->err != 0) {
\r
8270 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8272 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8277 if (appData.debugMode) {
\r
8278 fprintf(debugFP, "%s: %s\n", label, str);
\r
8280 if (appData.popupExitMessage) {
\r
8281 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8282 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8284 ExitEvent(exitStatus);
\r
8289 DisplayInformation(char *str)
\r
8291 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8296 DisplayNote(char *str)
\r
8298 ErrorPopUp(_("Note"), str);
\r
8303 char *title, *question, *replyPrefix;
\r
8308 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8310 static QuestionParams *qp;
\r
8311 char reply[MSG_SIZ];
\r
8314 switch (message) {
\r
8315 case WM_INITDIALOG:
\r
8316 qp = (QuestionParams *) lParam;
\r
8317 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8318 Translate(hDlg, DLG_Question);
\r
8319 SetWindowText(hDlg, qp->title);
\r
8320 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8321 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8325 switch (LOWORD(wParam)) {
\r
8327 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8328 if (*reply) strcat(reply, " ");
\r
8329 len = strlen(reply);
\r
8330 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8331 strcat(reply, "\n");
\r
8332 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8333 EndDialog(hDlg, TRUE);
\r
8334 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8337 EndDialog(hDlg, FALSE);
\r
8348 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8350 QuestionParams qp;
\r
8354 qp.question = question;
\r
8355 qp.replyPrefix = replyPrefix;
\r
8357 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8358 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8359 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8360 FreeProcInstance(lpProc);
\r
8363 /* [AS] Pick FRC position */
\r
8364 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8366 static int * lpIndexFRC;
\r
8372 case WM_INITDIALOG:
\r
8373 lpIndexFRC = (int *) lParam;
\r
8375 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8376 Translate(hDlg, DLG_NewGameFRC);
\r
8378 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8379 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8380 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8381 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8386 switch( LOWORD(wParam) ) {
\r
8388 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8389 EndDialog( hDlg, 0 );
\r
8390 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8393 EndDialog( hDlg, 1 );
\r
8395 case IDC_NFG_Edit:
\r
8396 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8397 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8399 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8402 case IDC_NFG_Random:
\r
8403 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8404 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8417 int index = appData.defaultFrcPosition;
\r
8418 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8420 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8422 if( result == 0 ) {
\r
8423 appData.defaultFrcPosition = index;
\r
8429 /* [AS] Game list options. Refactored by HGM */
\r
8431 HWND gameListOptionsDialog;
\r
8433 // low-level front-end: clear text edit / list widget
\r
8437 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8440 // low-level front-end: clear text edit / list widget
\r
8442 GLT_DeSelectList()
\r
8444 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8447 // low-level front-end: append line to text edit / list widget
\r
8449 GLT_AddToList( char *name )
\r
8452 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8456 // low-level front-end: get line from text edit / list widget
\r
8458 GLT_GetFromList( int index, char *name )
\r
8461 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8467 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8469 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8470 int idx2 = idx1 + delta;
\r
8471 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8473 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8476 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8477 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8478 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8479 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8483 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8487 case WM_INITDIALOG:
\r
8488 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8490 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8491 Translate(hDlg, DLG_GameListOptions);
\r
8493 /* Initialize list */
\r
8494 GLT_TagsToList( lpUserGLT );
\r
8496 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8501 switch( LOWORD(wParam) ) {
\r
8504 EndDialog( hDlg, 0 );
\r
8507 EndDialog( hDlg, 1 );
\r
8510 case IDC_GLT_Default:
\r
8511 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8514 case IDC_GLT_Restore:
\r
8515 GLT_TagsToList( appData.gameListTags );
\r
8519 GLT_MoveSelection( hDlg, -1 );
\r
8522 case IDC_GLT_Down:
\r
8523 GLT_MoveSelection( hDlg, +1 );
\r
8533 int GameListOptions()
\r
8536 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8538 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8540 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8542 if( result == 0 ) {
\r
8543 /* [AS] Memory leak here! */
\r
8544 appData.gameListTags = strdup( lpUserGLT );
\r
8551 DisplayIcsInteractionTitle(char *str)
\r
8553 char consoleTitle[MSG_SIZ];
\r
8555 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8556 SetWindowText(hwndConsole, consoleTitle);
\r
8560 DrawPosition(int fullRedraw, Board board)
\r
8562 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8565 void NotifyFrontendLogin()
\r
8568 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8574 fromX = fromY = -1;
\r
8575 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8576 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8577 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8578 dragInfo.lastpos = dragInfo.pos;
\r
8579 dragInfo.start.x = dragInfo.start.y = -1;
\r
8580 dragInfo.from = dragInfo.start;
\r
8582 DrawPosition(TRUE, NULL);
\r
8589 CommentPopUp(char *title, char *str)
\r
8591 HWND hwnd = GetActiveWindow();
\r
8592 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8594 SetActiveWindow(hwnd);
\r
8598 CommentPopDown(void)
\r
8600 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8601 if (commentDialog) {
\r
8602 ShowWindow(commentDialog, SW_HIDE);
\r
8604 commentUp = FALSE;
\r
8608 EditCommentPopUp(int index, char *title, char *str)
\r
8610 EitherCommentPopUp(index, title, str, TRUE);
\r
8617 MyPlaySound(&sounds[(int)SoundMove]);
\r
8620 VOID PlayIcsWinSound()
\r
8622 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8625 VOID PlayIcsLossSound()
\r
8627 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8630 VOID PlayIcsDrawSound()
\r
8632 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8635 VOID PlayIcsUnfinishedSound()
\r
8637 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8643 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8649 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8657 consoleEcho = TRUE;
\r
8658 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8659 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8660 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8669 consoleEcho = FALSE;
\r
8670 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8671 /* This works OK: set text and background both to the same color */
\r
8673 cf.crTextColor = COLOR_ECHOOFF;
\r
8674 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8675 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8678 /* No Raw()...? */
\r
8680 void Colorize(ColorClass cc, int continuation)
\r
8682 currentColorClass = cc;
\r
8683 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8684 consoleCF.crTextColor = textAttribs[cc].color;
\r
8685 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8686 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8692 static char buf[MSG_SIZ];
\r
8693 DWORD bufsiz = MSG_SIZ;
\r
8695 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8696 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8698 if (!GetUserName(buf, &bufsiz)) {
\r
8699 /*DisplayError("Error getting user name", GetLastError());*/
\r
8700 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8708 static char buf[MSG_SIZ];
\r
8709 DWORD bufsiz = MSG_SIZ;
\r
8711 if (!GetComputerName(buf, &bufsiz)) {
\r
8712 /*DisplayError("Error getting host name", GetLastError());*/
\r
8713 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8720 ClockTimerRunning()
\r
8722 return clockTimerEvent != 0;
\r
8728 if (clockTimerEvent == 0) return FALSE;
\r
8729 KillTimer(hwndMain, clockTimerEvent);
\r
8730 clockTimerEvent = 0;
\r
8735 StartClockTimer(long millisec)
\r
8737 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8738 (UINT) millisec, NULL);
\r
8742 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8745 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8747 if(appData.noGUI) return;
\r
8748 hdc = GetDC(hwndMain);
\r
8749 if (!IsIconic(hwndMain)) {
\r
8750 DisplayAClock(hdc, timeRemaining, highlight,
\r
8751 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8753 if (highlight && iconCurrent == iconBlack) {
\r
8754 iconCurrent = iconWhite;
\r
8755 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8756 if (IsIconic(hwndMain)) {
\r
8757 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8760 (void) ReleaseDC(hwndMain, hdc);
\r
8762 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8766 DisplayBlackClock(long timeRemaining, int highlight)
\r
8769 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8771 if(appData.noGUI) return;
\r
8772 hdc = GetDC(hwndMain);
\r
8773 if (!IsIconic(hwndMain)) {
\r
8774 DisplayAClock(hdc, timeRemaining, highlight,
\r
8775 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8777 if (highlight && iconCurrent == iconWhite) {
\r
8778 iconCurrent = iconBlack;
\r
8779 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8780 if (IsIconic(hwndMain)) {
\r
8781 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8784 (void) ReleaseDC(hwndMain, hdc);
\r
8786 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8791 LoadGameTimerRunning()
\r
8793 return loadGameTimerEvent != 0;
\r
8797 StopLoadGameTimer()
\r
8799 if (loadGameTimerEvent == 0) return FALSE;
\r
8800 KillTimer(hwndMain, loadGameTimerEvent);
\r
8801 loadGameTimerEvent = 0;
\r
8806 StartLoadGameTimer(long millisec)
\r
8808 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8809 (UINT) millisec, NULL);
\r
8817 char fileTitle[MSG_SIZ];
\r
8819 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8820 f = OpenFileDialog(hwndMain, "a", defName,
\r
8821 appData.oldSaveStyle ? "gam" : "pgn",
\r
8823 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8825 SaveGame(f, 0, "");
\r
8832 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8834 if (delayedTimerEvent != 0) {
\r
8835 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8836 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8838 KillTimer(hwndMain, delayedTimerEvent);
\r
8839 delayedTimerEvent = 0;
\r
8840 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8841 delayedTimerCallback();
\r
8843 delayedTimerCallback = cb;
\r
8844 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8845 (UINT) millisec, NULL);
\r
8848 DelayedEventCallback
\r
8851 if (delayedTimerEvent) {
\r
8852 return delayedTimerCallback;
\r
8859 CancelDelayedEvent()
\r
8861 if (delayedTimerEvent) {
\r
8862 KillTimer(hwndMain, delayedTimerEvent);
\r
8863 delayedTimerEvent = 0;
\r
8867 DWORD GetWin32Priority(int nice)
\r
8868 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8870 REALTIME_PRIORITY_CLASS 0x00000100
\r
8871 HIGH_PRIORITY_CLASS 0x00000080
\r
8872 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8873 NORMAL_PRIORITY_CLASS 0x00000020
\r
8874 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8875 IDLE_PRIORITY_CLASS 0x00000040
\r
8877 if (nice < -15) return 0x00000080;
\r
8878 if (nice < 0) return 0x00008000;
\r
8879 if (nice == 0) return 0x00000020;
\r
8880 if (nice < 15) return 0x00004000;
\r
8881 return 0x00000040;
\r
8884 void RunCommand(char *cmdLine)
\r
8886 /* Now create the child process. */
\r
8887 STARTUPINFO siStartInfo;
\r
8888 PROCESS_INFORMATION piProcInfo;
\r
8890 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8891 siStartInfo.lpReserved = NULL;
\r
8892 siStartInfo.lpDesktop = NULL;
\r
8893 siStartInfo.lpTitle = NULL;
\r
8894 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8895 siStartInfo.cbReserved2 = 0;
\r
8896 siStartInfo.lpReserved2 = NULL;
\r
8897 siStartInfo.hStdInput = NULL;
\r
8898 siStartInfo.hStdOutput = NULL;
\r
8899 siStartInfo.hStdError = NULL;
\r
8901 CreateProcess(NULL,
\r
8902 cmdLine, /* command line */
\r
8903 NULL, /* process security attributes */
\r
8904 NULL, /* primary thread security attrs */
\r
8905 TRUE, /* handles are inherited */
\r
8906 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8907 NULL, /* use parent's environment */
\r
8909 &siStartInfo, /* STARTUPINFO pointer */
\r
8910 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8912 CloseHandle(piProcInfo.hThread);
\r
8915 /* Start a child process running the given program.
\r
8916 The process's standard output can be read from "from", and its
\r
8917 standard input can be written to "to".
\r
8918 Exit with fatal error if anything goes wrong.
\r
8919 Returns an opaque pointer that can be used to destroy the process
\r
8923 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8925 #define BUFSIZE 4096
\r
8927 HANDLE hChildStdinRd, hChildStdinWr,
\r
8928 hChildStdoutRd, hChildStdoutWr;
\r
8929 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8930 SECURITY_ATTRIBUTES saAttr;
\r
8932 PROCESS_INFORMATION piProcInfo;
\r
8933 STARTUPINFO siStartInfo;
\r
8935 char buf[MSG_SIZ];
\r
8938 if (appData.debugMode) {
\r
8939 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8944 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8945 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8946 saAttr.bInheritHandle = TRUE;
\r
8947 saAttr.lpSecurityDescriptor = NULL;
\r
8950 * The steps for redirecting child's STDOUT:
\r
8951 * 1. Create anonymous pipe to be STDOUT for child.
\r
8952 * 2. Create a noninheritable duplicate of read handle,
\r
8953 * and close the inheritable read handle.
\r
8956 /* Create a pipe for the child's STDOUT. */
\r
8957 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8958 return GetLastError();
\r
8961 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8962 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8963 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8964 FALSE, /* not inherited */
\r
8965 DUPLICATE_SAME_ACCESS);
\r
8967 return GetLastError();
\r
8969 CloseHandle(hChildStdoutRd);
\r
8972 * The steps for redirecting child's STDIN:
\r
8973 * 1. Create anonymous pipe to be STDIN for child.
\r
8974 * 2. Create a noninheritable duplicate of write handle,
\r
8975 * and close the inheritable write handle.
\r
8978 /* Create a pipe for the child's STDIN. */
\r
8979 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8980 return GetLastError();
\r
8983 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8984 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8985 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8986 FALSE, /* not inherited */
\r
8987 DUPLICATE_SAME_ACCESS);
\r
8989 return GetLastError();
\r
8991 CloseHandle(hChildStdinWr);
\r
8993 /* Arrange to (1) look in dir for the child .exe file, and
\r
8994 * (2) have dir be the child's working directory. Interpret
\r
8995 * dir relative to the directory WinBoard loaded from. */
\r
8996 GetCurrentDirectory(MSG_SIZ, buf);
\r
8997 SetCurrentDirectory(installDir);
\r
8998 SetCurrentDirectory(dir);
\r
9000 /* Now create the child process. */
\r
9002 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9003 siStartInfo.lpReserved = NULL;
\r
9004 siStartInfo.lpDesktop = NULL;
\r
9005 siStartInfo.lpTitle = NULL;
\r
9006 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9007 siStartInfo.cbReserved2 = 0;
\r
9008 siStartInfo.lpReserved2 = NULL;
\r
9009 siStartInfo.hStdInput = hChildStdinRd;
\r
9010 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9011 siStartInfo.hStdError = hChildStdoutWr;
\r
9013 fSuccess = CreateProcess(NULL,
\r
9014 cmdLine, /* command line */
\r
9015 NULL, /* process security attributes */
\r
9016 NULL, /* primary thread security attrs */
\r
9017 TRUE, /* handles are inherited */
\r
9018 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9019 NULL, /* use parent's environment */
\r
9021 &siStartInfo, /* STARTUPINFO pointer */
\r
9022 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9024 err = GetLastError();
\r
9025 SetCurrentDirectory(buf); /* return to prev directory */
\r
9030 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9031 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9032 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9035 /* Close the handles we don't need in the parent */
\r
9036 CloseHandle(piProcInfo.hThread);
\r
9037 CloseHandle(hChildStdinRd);
\r
9038 CloseHandle(hChildStdoutWr);
\r
9040 /* Prepare return value */
\r
9041 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9042 cp->kind = CPReal;
\r
9043 cp->hProcess = piProcInfo.hProcess;
\r
9044 cp->pid = piProcInfo.dwProcessId;
\r
9045 cp->hFrom = hChildStdoutRdDup;
\r
9046 cp->hTo = hChildStdinWrDup;
\r
9048 *pr = (void *) cp;
\r
9050 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9051 2000 where engines sometimes don't see the initial command(s)
\r
9052 from WinBoard and hang. I don't understand how that can happen,
\r
9053 but the Sleep is harmless, so I've put it in. Others have also
\r
9054 reported what may be the same problem, so hopefully this will fix
\r
9055 it for them too. */
\r
9063 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9065 ChildProc *cp; int result;
\r
9067 cp = (ChildProc *) pr;
\r
9068 if (cp == NULL) return;
\r
9070 switch (cp->kind) {
\r
9072 /* TerminateProcess is considered harmful, so... */
\r
9073 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9074 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9075 /* The following doesn't work because the chess program
\r
9076 doesn't "have the same console" as WinBoard. Maybe
\r
9077 we could arrange for this even though neither WinBoard
\r
9078 nor the chess program uses a console for stdio? */
\r
9079 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9081 /* [AS] Special termination modes for misbehaving programs... */
\r
9082 if( signal == 9 ) {
\r
9083 result = TerminateProcess( cp->hProcess, 0 );
\r
9085 if ( appData.debugMode) {
\r
9086 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9089 else if( signal == 10 ) {
\r
9090 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9092 if( dw != WAIT_OBJECT_0 ) {
\r
9093 result = TerminateProcess( cp->hProcess, 0 );
\r
9095 if ( appData.debugMode) {
\r
9096 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9102 CloseHandle(cp->hProcess);
\r
9106 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9110 closesocket(cp->sock);
\r
9115 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9116 closesocket(cp->sock);
\r
9117 closesocket(cp->sock2);
\r
9125 InterruptChildProcess(ProcRef pr)
\r
9129 cp = (ChildProc *) pr;
\r
9130 if (cp == NULL) return;
\r
9131 switch (cp->kind) {
\r
9133 /* The following doesn't work because the chess program
\r
9134 doesn't "have the same console" as WinBoard. Maybe
\r
9135 we could arrange for this even though neither WinBoard
\r
9136 nor the chess program uses a console for stdio */
\r
9137 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9142 /* Can't interrupt */
\r
9146 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9153 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9155 char cmdLine[MSG_SIZ];
\r
9157 if (port[0] == NULLCHAR) {
\r
9158 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9160 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9162 return StartChildProcess(cmdLine, "", pr);
\r
9166 /* Code to open TCP sockets */
\r
9169 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9174 struct sockaddr_in sa, mysa;
\r
9175 struct hostent FAR *hp;
\r
9176 unsigned short uport;
\r
9177 WORD wVersionRequested;
\r
9180 /* Initialize socket DLL */
\r
9181 wVersionRequested = MAKEWORD(1, 1);
\r
9182 err = WSAStartup(wVersionRequested, &wsaData);
\r
9183 if (err != 0) return err;
\r
9186 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9187 err = WSAGetLastError();
\r
9192 /* Bind local address using (mostly) don't-care values.
\r
9194 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9195 mysa.sin_family = AF_INET;
\r
9196 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9197 uport = (unsigned short) 0;
\r
9198 mysa.sin_port = htons(uport);
\r
9199 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9200 == SOCKET_ERROR) {
\r
9201 err = WSAGetLastError();
\r
9206 /* Resolve remote host name */
\r
9207 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9208 if (!(hp = gethostbyname(host))) {
\r
9209 unsigned int b0, b1, b2, b3;
\r
9211 err = WSAGetLastError();
\r
9213 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9214 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9215 hp->h_addrtype = AF_INET;
\r
9217 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9218 hp->h_addr_list[0] = (char *) malloc(4);
\r
9219 hp->h_addr_list[0][0] = (char) b0;
\r
9220 hp->h_addr_list[0][1] = (char) b1;
\r
9221 hp->h_addr_list[0][2] = (char) b2;
\r
9222 hp->h_addr_list[0][3] = (char) b3;
\r
9228 sa.sin_family = hp->h_addrtype;
\r
9229 uport = (unsigned short) atoi(port);
\r
9230 sa.sin_port = htons(uport);
\r
9231 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9233 /* Make connection */
\r
9234 if (connect(s, (struct sockaddr *) &sa,
\r
9235 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9236 err = WSAGetLastError();
\r
9241 /* Prepare return value */
\r
9242 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9243 cp->kind = CPSock;
\r
9245 *pr = (ProcRef *) cp;
\r
9251 OpenCommPort(char *name, ProcRef *pr)
\r
9256 char fullname[MSG_SIZ];
\r
9258 if (*name != '\\')
\r
9259 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9261 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9263 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9264 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9265 if (h == (HANDLE) -1) {
\r
9266 return GetLastError();
\r
9270 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9272 /* Accumulate characters until a 100ms pause, then parse */
\r
9273 ct.ReadIntervalTimeout = 100;
\r
9274 ct.ReadTotalTimeoutMultiplier = 0;
\r
9275 ct.ReadTotalTimeoutConstant = 0;
\r
9276 ct.WriteTotalTimeoutMultiplier = 0;
\r
9277 ct.WriteTotalTimeoutConstant = 0;
\r
9278 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9280 /* Prepare return value */
\r
9281 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9282 cp->kind = CPComm;
\r
9285 *pr = (ProcRef *) cp;
\r
9291 OpenLoopback(ProcRef *pr)
\r
9293 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9299 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9304 struct sockaddr_in sa, mysa;
\r
9305 struct hostent FAR *hp;
\r
9306 unsigned short uport;
\r
9307 WORD wVersionRequested;
\r
9310 char stderrPortStr[MSG_SIZ];
\r
9312 /* Initialize socket DLL */
\r
9313 wVersionRequested = MAKEWORD(1, 1);
\r
9314 err = WSAStartup(wVersionRequested, &wsaData);
\r
9315 if (err != 0) return err;
\r
9317 /* Resolve remote host name */
\r
9318 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9319 if (!(hp = gethostbyname(host))) {
\r
9320 unsigned int b0, b1, b2, b3;
\r
9322 err = WSAGetLastError();
\r
9324 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9325 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9326 hp->h_addrtype = AF_INET;
\r
9328 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9329 hp->h_addr_list[0] = (char *) malloc(4);
\r
9330 hp->h_addr_list[0][0] = (char) b0;
\r
9331 hp->h_addr_list[0][1] = (char) b1;
\r
9332 hp->h_addr_list[0][2] = (char) b2;
\r
9333 hp->h_addr_list[0][3] = (char) b3;
\r
9339 sa.sin_family = hp->h_addrtype;
\r
9340 uport = (unsigned short) 514;
\r
9341 sa.sin_port = htons(uport);
\r
9342 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9344 /* Bind local socket to unused "privileged" port address
\r
9346 s = INVALID_SOCKET;
\r
9347 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9348 mysa.sin_family = AF_INET;
\r
9349 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9350 for (fromPort = 1023;; fromPort--) {
\r
9351 if (fromPort < 0) {
\r
9353 return WSAEADDRINUSE;
\r
9355 if (s == INVALID_SOCKET) {
\r
9356 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9357 err = WSAGetLastError();
\r
9362 uport = (unsigned short) fromPort;
\r
9363 mysa.sin_port = htons(uport);
\r
9364 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9365 == SOCKET_ERROR) {
\r
9366 err = WSAGetLastError();
\r
9367 if (err == WSAEADDRINUSE) continue;
\r
9371 if (connect(s, (struct sockaddr *) &sa,
\r
9372 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9373 err = WSAGetLastError();
\r
9374 if (err == WSAEADDRINUSE) {
\r
9385 /* Bind stderr local socket to unused "privileged" port address
\r
9387 s2 = INVALID_SOCKET;
\r
9388 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9389 mysa.sin_family = AF_INET;
\r
9390 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9391 for (fromPort = 1023;; fromPort--) {
\r
9392 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9393 if (fromPort < 0) {
\r
9394 (void) closesocket(s);
\r
9396 return WSAEADDRINUSE;
\r
9398 if (s2 == INVALID_SOCKET) {
\r
9399 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9400 err = WSAGetLastError();
\r
9406 uport = (unsigned short) fromPort;
\r
9407 mysa.sin_port = htons(uport);
\r
9408 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9409 == SOCKET_ERROR) {
\r
9410 err = WSAGetLastError();
\r
9411 if (err == WSAEADDRINUSE) continue;
\r
9412 (void) closesocket(s);
\r
9416 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9417 err = WSAGetLastError();
\r
9418 if (err == WSAEADDRINUSE) {
\r
9420 s2 = INVALID_SOCKET;
\r
9423 (void) closesocket(s);
\r
9424 (void) closesocket(s2);
\r
9430 prevStderrPort = fromPort; // remember port used
\r
9431 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9433 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9434 err = WSAGetLastError();
\r
9435 (void) closesocket(s);
\r
9436 (void) closesocket(s2);
\r
9441 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9442 err = WSAGetLastError();
\r
9443 (void) closesocket(s);
\r
9444 (void) closesocket(s2);
\r
9448 if (*user == NULLCHAR) user = UserName();
\r
9449 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9450 err = WSAGetLastError();
\r
9451 (void) closesocket(s);
\r
9452 (void) closesocket(s2);
\r
9456 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9457 err = WSAGetLastError();
\r
9458 (void) closesocket(s);
\r
9459 (void) closesocket(s2);
\r
9464 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9465 err = WSAGetLastError();
\r
9466 (void) closesocket(s);
\r
9467 (void) closesocket(s2);
\r
9471 (void) closesocket(s2); /* Stop listening */
\r
9473 /* Prepare return value */
\r
9474 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9475 cp->kind = CPRcmd;
\r
9478 *pr = (ProcRef *) cp;
\r
9485 AddInputSource(ProcRef pr, int lineByLine,
\r
9486 InputCallback func, VOIDSTAR closure)
\r
9488 InputSource *is, *is2 = NULL;
\r
9489 ChildProc *cp = (ChildProc *) pr;
\r
9491 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9492 is->lineByLine = lineByLine;
\r
9494 is->closure = closure;
\r
9495 is->second = NULL;
\r
9496 is->next = is->buf;
\r
9497 if (pr == NoProc) {
\r
9498 is->kind = CPReal;
\r
9499 consoleInputSource = is;
\r
9501 is->kind = cp->kind;
\r
9503 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9504 we create all threads suspended so that the is->hThread variable can be
\r
9505 safely assigned, then let the threads start with ResumeThread.
\r
9507 switch (cp->kind) {
\r
9509 is->hFile = cp->hFrom;
\r
9510 cp->hFrom = NULL; /* now owned by InputThread */
\r
9512 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9513 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9517 is->hFile = cp->hFrom;
\r
9518 cp->hFrom = NULL; /* now owned by InputThread */
\r
9520 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9521 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9525 is->sock = cp->sock;
\r
9527 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9528 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9532 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9534 is->sock = cp->sock;
\r
9536 is2->sock = cp->sock2;
\r
9537 is2->second = is2;
\r
9539 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9540 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9542 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9543 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9547 if( is->hThread != NULL ) {
\r
9548 ResumeThread( is->hThread );
\r
9551 if( is2 != NULL && is2->hThread != NULL ) {
\r
9552 ResumeThread( is2->hThread );
\r
9556 return (InputSourceRef) is;
\r
9560 RemoveInputSource(InputSourceRef isr)
\r
9564 is = (InputSource *) isr;
\r
9565 is->hThread = NULL; /* tell thread to stop */
\r
9566 CloseHandle(is->hThread);
\r
9567 if (is->second != NULL) {
\r
9568 is->second->hThread = NULL;
\r
9569 CloseHandle(is->second->hThread);
\r
9573 int no_wrap(char *message, int count)
\r
9575 ConsoleOutput(message, count, FALSE);
\r
9580 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9583 int outCount = SOCKET_ERROR;
\r
9584 ChildProc *cp = (ChildProc *) pr;
\r
9585 static OVERLAPPED ovl;
\r
9586 static int line = 0;
\r
9590 if (appData.noJoin || !appData.useInternalWrap)
\r
9591 return no_wrap(message, count);
\r
9594 int width = get_term_width();
\r
9595 int len = wrap(NULL, message, count, width, &line);
\r
9596 char *msg = malloc(len);
\r
9600 return no_wrap(message, count);
\r
9603 dbgchk = wrap(msg, message, count, width, &line);
\r
9604 if (dbgchk != len && appData.debugMode)
\r
9605 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9606 ConsoleOutput(msg, len, FALSE);
\r
9613 if (ovl.hEvent == NULL) {
\r
9614 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9616 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9618 switch (cp->kind) {
\r
9621 outCount = send(cp->sock, message, count, 0);
\r
9622 if (outCount == SOCKET_ERROR) {
\r
9623 *outError = WSAGetLastError();
\r
9625 *outError = NO_ERROR;
\r
9630 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9631 &dOutCount, NULL)) {
\r
9632 *outError = NO_ERROR;
\r
9633 outCount = (int) dOutCount;
\r
9635 *outError = GetLastError();
\r
9640 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9641 &dOutCount, &ovl);
\r
9642 if (*outError == NO_ERROR) {
\r
9643 outCount = (int) dOutCount;
\r
9653 if(n != 0) Sleep(n);
\r
9657 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9660 /* Ignore delay, not implemented for WinBoard */
\r
9661 return OutputToProcess(pr, message, count, outError);
\r
9666 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9667 char *buf, int count, int error)
\r
9669 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9672 /* see wgamelist.c for Game List functions */
\r
9673 /* see wedittags.c for Edit Tags functions */
\r
9680 char buf[MSG_SIZ];
\r
9683 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9684 f = fopen(buf, "r");
\r
9686 ProcessICSInitScript(f);
\r
9694 StartAnalysisClock()
\r
9696 if (analysisTimerEvent) return;
\r
9697 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9698 (UINT) 2000, NULL);
\r
9702 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9704 highlightInfo.sq[0].x = fromX;
\r
9705 highlightInfo.sq[0].y = fromY;
\r
9706 highlightInfo.sq[1].x = toX;
\r
9707 highlightInfo.sq[1].y = toY;
\r
9713 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9714 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9718 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9720 premoveHighlightInfo.sq[0].x = fromX;
\r
9721 premoveHighlightInfo.sq[0].y = fromY;
\r
9722 premoveHighlightInfo.sq[1].x = toX;
\r
9723 premoveHighlightInfo.sq[1].y = toY;
\r
9727 ClearPremoveHighlights()
\r
9729 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9730 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9734 ShutDownFrontEnd()
\r
9736 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9737 DeleteClipboardTempFiles();
\r
9743 if (IsIconic(hwndMain))
\r
9744 ShowWindow(hwndMain, SW_RESTORE);
\r
9746 SetActiveWindow(hwndMain);
\r
9750 * Prototypes for animation support routines
\r
9752 static void ScreenSquare(int column, int row, POINT * pt);
\r
9753 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9754 POINT frames[], int * nFrames);
\r
9760 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9761 { // [HGM] atomic: animate blast wave
\r
9764 explodeInfo.fromX = fromX;
\r
9765 explodeInfo.fromY = fromY;
\r
9766 explodeInfo.toX = toX;
\r
9767 explodeInfo.toY = toY;
\r
9768 for(i=1; i<4*kFactor; i++) {
\r
9769 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9770 DrawPosition(FALSE, board);
\r
9771 Sleep(appData.animSpeed);
\r
9773 explodeInfo.radius = 0;
\r
9774 DrawPosition(TRUE, board);
\r
9778 AnimateMove(board, fromX, fromY, toX, toY)
\r
9785 ChessSquare piece;
\r
9786 POINT start, finish, mid;
\r
9787 POINT frames[kFactor * 2 + 1];
\r
9790 if (!appData.animate) return;
\r
9791 if (doingSizing) return;
\r
9792 if (fromY < 0 || fromX < 0) return;
\r
9793 piece = board[fromY][fromX];
\r
9794 if (piece >= EmptySquare) return;
\r
9796 ScreenSquare(fromX, fromY, &start);
\r
9797 ScreenSquare(toX, toY, &finish);
\r
9799 /* All moves except knight jumps move in straight line */
\r
9800 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9801 mid.x = start.x + (finish.x - start.x) / 2;
\r
9802 mid.y = start.y + (finish.y - start.y) / 2;
\r
9804 /* Knight: make straight movement then diagonal */
\r
9805 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9806 mid.x = start.x + (finish.x - start.x) / 2;
\r
9810 mid.y = start.y + (finish.y - start.y) / 2;
\r
9814 /* Don't use as many frames for very short moves */
\r
9815 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9816 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9818 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9820 animInfo.from.x = fromX;
\r
9821 animInfo.from.y = fromY;
\r
9822 animInfo.to.x = toX;
\r
9823 animInfo.to.y = toY;
\r
9824 animInfo.lastpos = start;
\r
9825 animInfo.piece = piece;
\r
9826 for (n = 0; n < nFrames; n++) {
\r
9827 animInfo.pos = frames[n];
\r
9828 DrawPosition(FALSE, NULL);
\r
9829 animInfo.lastpos = animInfo.pos;
\r
9830 Sleep(appData.animSpeed);
\r
9832 animInfo.pos = finish;
\r
9833 DrawPosition(FALSE, NULL);
\r
9834 animInfo.piece = EmptySquare;
\r
9835 Explode(board, fromX, fromY, toX, toY);
\r
9838 /* Convert board position to corner of screen rect and color */
\r
9841 ScreenSquare(column, row, pt)
\r
9842 int column; int row; POINT * pt;
\r
9845 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9846 pt->y = lineGap + row * (squareSize + lineGap);
\r
9848 pt->x = lineGap + column * (squareSize + lineGap);
\r
9849 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9853 /* Generate a series of frame coords from start->mid->finish.
\r
9854 The movement rate doubles until the half way point is
\r
9855 reached, then halves back down to the final destination,
\r
9856 which gives a nice slow in/out effect. The algorithmn
\r
9857 may seem to generate too many intermediates for short
\r
9858 moves, but remember that the purpose is to attract the
\r
9859 viewers attention to the piece about to be moved and
\r
9860 then to where it ends up. Too few frames would be less
\r
9864 Tween(start, mid, finish, factor, frames, nFrames)
\r
9865 POINT * start; POINT * mid;
\r
9866 POINT * finish; int factor;
\r
9867 POINT frames[]; int * nFrames;
\r
9869 int n, fraction = 1, count = 0;
\r
9871 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9872 for (n = 0; n < factor; n++)
\r
9874 for (n = 0; n < factor; n++) {
\r
9875 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9876 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9878 fraction = fraction / 2;
\r
9882 frames[count] = *mid;
\r
9885 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9887 for (n = 0; n < factor; n++) {
\r
9888 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9889 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9891 fraction = fraction * 2;
\r
9897 SettingsPopUp(ChessProgramState *cps)
\r
9898 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9899 EngineOptionsPopup(savedHwnd, cps);
\r
9902 int flock(int fid, int code)
\r
9904 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9908 ov.OffsetHigh = 0;
\r
9910 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9911 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9912 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9913 default: return -1;
\r