2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][42] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
257 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
259 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
262 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
265 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
266 { ABOUTBOX2, IDC_ChessBoard },
\r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
268 OPT_GameListClose, IDC_GameListDoFilter },
\r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
270 { DLG_Error, IDOK },
\r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
272 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
275 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
276 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
277 { DLG_IndexNumber, IDC_Index },
\r
278 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
279 { DLG_TypeInName, IDOK, IDCANCEL },
\r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
281 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
283 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
284 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
285 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
286 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
287 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
288 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
290 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
291 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
292 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
293 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
294 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
295 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
296 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
297 GPB_General, GPB_Alarm },
\r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
299 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
300 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
301 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
302 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
303 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
304 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
305 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
307 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
308 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
309 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
310 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
311 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
312 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
313 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
314 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
316 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
317 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
318 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
319 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
322 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
323 { DLG_MoveHistory },
\r
324 { DLG_EvalGraph },
\r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
328 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
329 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
330 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
332 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
333 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
337 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
338 static int lastChecked;
\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
340 extern int tinyLayout;
\r
341 extern char * menuBarText[][10];
\r
344 LoadLanguageFile(char *name)
\r
345 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
347 int i=0, j=0, n=0, k;
\r
350 if(!name || name[0] == NULLCHAR) return;
\r
351 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
352 appData.language = oldLanguage;
\r
353 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
354 if((f = fopen(buf, "r")) == NULL) return;
\r
355 while((k = fgetc(f)) != EOF) {
\r
356 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
357 languageBuf[i] = k;
\r
359 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
361 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
362 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
363 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
364 english[j] = languageBuf + n + 1; *p = 0;
\r
365 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
371 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
373 case 'n': k = '\n'; break;
\r
374 case 'r': k = '\r'; break;
\r
375 case 't': k = '\t'; break;
\r
377 languageBuf[--i] = k;
\r
382 barbaric = (j != 0);
\r
383 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
388 { // return the translation of the given string
\r
389 // efficiency can be improved a lot...
\r
391 static char buf[MSG_SIZ];
\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
393 if(!barbaric) return s;
\r
394 if(!s) return ""; // sanity
\r
395 while(english[i]) {
\r
396 if(!strcmp(s, english[i])) return foreign[i];
\r
397 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
398 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
407 Translate(HWND hDlg, int dialogID)
\r
408 { // translate all text items in the given dialog
\r
410 char buf[MSG_SIZ], *s;
\r
411 if(!barbaric) return;
\r
412 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
413 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
414 GetWindowText( hDlg, buf, MSG_SIZ );
\r
416 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
417 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
418 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
419 if(strlen(buf) == 0) continue;
\r
421 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
426 TranslateOneMenu(int i, HMENU subMenu)
\r
429 static MENUITEMINFO info;
\r
431 info.cbSize = sizeof(MENUITEMINFO);
\r
432 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
433 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
435 info.dwTypeData = buf;
\r
436 info.cch = sizeof(buf);
\r
437 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
439 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
440 else menuText[i][j] = strdup(buf); // remember original on first change
\r
442 if(buf[0] == NULLCHAR) continue;
\r
443 info.dwTypeData = T_(buf);
\r
444 info.cch = strlen(buf)+1;
\r
445 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
451 TranslateMenus(int addLanguage)
\r
454 WIN32_FIND_DATA fileData;
\r
456 #define IDM_English 1970
\r
458 HMENU mainMenu = GetMenu(hwndMain);
\r
459 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
460 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
461 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
462 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
463 TranslateOneMenu(i, subMenu);
\r
465 DrawMenuBar(hwndMain);
\r
468 if(!addLanguage) return;
\r
469 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
470 HMENU mainMenu = GetMenu(hwndMain);
\r
471 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
472 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
473 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
474 i = 0; lastChecked = IDM_English;
\r
476 char *p, *q = fileData.cFileName;
\r
477 int checkFlag = MF_UNCHECKED;
\r
478 languageFile[i] = strdup(q);
\r
479 if(barbaric && !strcmp(oldLanguage, q)) {
\r
480 checkFlag = MF_CHECKED;
\r
481 lastChecked = IDM_English + i + 1;
\r
482 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
484 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
485 p = strstr(fileData.cFileName, ".lng");
\r
487 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
488 } while(FindNextFile(hFind, &fileData));
\r
495 #define IDM_RecentEngines 3000
\r
498 RecentEngineMenu (char *s)
\r
500 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
501 HMENU mainMenu = GetMenu(hwndMain);
\r
502 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
503 int i=IDM_RecentEngines;
\r
504 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
505 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
507 char *p = strchr(s, '\n');
\r
508 if(p == NULL) return; // malformed!
\r
510 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
524 int cliWidth, cliHeight;
\r
527 SizeInfo sizeInfo[] =
\r
529 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
530 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
531 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
532 { "petite", 33, 1, 1, 1, 0, 0 },
\r
533 { "slim", 37, 2, 1, 0, 0, 0 },
\r
534 { "small", 40, 2, 1, 0, 0, 0 },
\r
535 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
536 { "middling", 49, 2, 0, 0, 0, 0 },
\r
537 { "average", 54, 2, 0, 0, 0, 0 },
\r
538 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
539 { "medium", 64, 3, 0, 0, 0, 0 },
\r
540 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
541 { "large", 80, 3, 0, 0, 0, 0 },
\r
542 { "big", 87, 3, 0, 0, 0, 0 },
\r
543 { "huge", 95, 3, 0, 0, 0, 0 },
\r
544 { "giant", 108, 3, 0, 0, 0, 0 },
\r
545 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
546 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
547 { NULL, 0, 0, 0, 0, 0, 0 }
\r
550 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
551 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
553 { 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
554 { 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
555 { 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
556 { 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
557 { 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
558 { 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
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
573 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
582 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
583 #define N_BUTTONS 5
\r
585 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
587 {"<<", IDM_ToStart, NULL, NULL},
\r
588 {"<", IDM_Backward, NULL, NULL},
\r
589 {"P", IDM_Pause, NULL, NULL},
\r
590 {">", IDM_Forward, NULL, NULL},
\r
591 {">>", IDM_ToEnd, NULL, NULL},
\r
594 int tinyLayout = 0, smallLayout = 0;
\r
595 #define MENU_BAR_ITEMS 9
\r
596 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
597 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
598 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
602 MySound sounds[(int)NSoundClasses];
\r
603 MyTextAttribs textAttribs[(int)NColorClasses];
\r
605 MyColorizeAttribs colorizeAttribs[] = {
\r
606 { (COLORREF)0, 0, N_("Shout Text") },
\r
607 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
608 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
609 { (COLORREF)0, 0, N_("Channel Text") },
\r
610 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
611 { (COLORREF)0, 0, N_("Tell Text") },
\r
612 { (COLORREF)0, 0, N_("Challenge Text") },
\r
613 { (COLORREF)0, 0, N_("Request Text") },
\r
614 { (COLORREF)0, 0, N_("Seek Text") },
\r
615 { (COLORREF)0, 0, N_("Normal Text") },
\r
616 { (COLORREF)0, 0, N_("None") }
\r
621 static char *commentTitle;
\r
622 static char *commentText;
\r
623 static int commentIndex;
\r
624 static Boolean editComment = FALSE;
\r
627 char errorTitle[MSG_SIZ];
\r
628 char errorMessage[2*MSG_SIZ];
\r
629 HWND errorDialog = NULL;
\r
630 BOOLEAN moveErrorMessageUp = FALSE;
\r
631 BOOLEAN consoleEcho = TRUE;
\r
632 CHARFORMAT consoleCF;
\r
633 COLORREF consoleBackgroundColor;
\r
635 char *programVersion;
\r
641 typedef int CPKind;
\r
650 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
653 #define INPUT_SOURCE_BUF_SIZE 4096
\r
655 typedef struct _InputSource {
\r
662 char buf[INPUT_SOURCE_BUF_SIZE];
\r
666 InputCallback func;
\r
667 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
671 InputSource *consoleInputSource;
\r
676 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
677 VOID ConsoleCreate();
\r
679 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
680 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
681 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
682 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
684 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
685 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
686 void ParseIcsTextMenu(char *icsTextMenuString);
\r
687 VOID PopUpNameDialog(char firstchar);
\r
688 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
692 int GameListOptions();
\r
694 int dummy; // [HGM] for obsolete args
\r
696 HWND hwndMain = NULL; /* root window*/
\r
697 HWND hwndConsole = NULL;
\r
698 HWND commentDialog = NULL;
\r
699 HWND moveHistoryDialog = NULL;
\r
700 HWND evalGraphDialog = NULL;
\r
701 HWND engineOutputDialog = NULL;
\r
702 HWND gameListDialog = NULL;
\r
703 HWND editTagsDialog = NULL;
\r
705 int commentUp = FALSE;
\r
707 WindowPlacement wpMain;
\r
708 WindowPlacement wpConsole;
\r
709 WindowPlacement wpComment;
\r
710 WindowPlacement wpMoveHistory;
\r
711 WindowPlacement wpEvalGraph;
\r
712 WindowPlacement wpEngineOutput;
\r
713 WindowPlacement wpGameList;
\r
714 WindowPlacement wpTags;
\r
716 VOID EngineOptionsPopup(); // [HGM] settings
\r
718 VOID GothicPopUp(char *title, VariantClass variant);
\r
720 * Setting "frozen" should disable all user input other than deleting
\r
721 * the window. We do this while engines are initializing themselves.
\r
723 static int frozen = 0;
\r
724 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
730 if (frozen) return;
\r
732 hmenu = GetMenu(hwndMain);
\r
733 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
734 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
736 DrawMenuBar(hwndMain);
\r
739 /* Undo a FreezeUI */
\r
745 if (!frozen) return;
\r
747 hmenu = GetMenu(hwndMain);
\r
748 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
749 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
751 DrawMenuBar(hwndMain);
\r
754 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
756 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
762 #define JAWS_ALT_INTERCEPT
\r
763 #define JAWS_KBUP_NAVIGATION
\r
764 #define JAWS_KBDOWN_NAVIGATION
\r
765 #define JAWS_MENU_ITEMS
\r
766 #define JAWS_SILENCE
\r
767 #define JAWS_REPLAY
\r
769 #define JAWS_COPYRIGHT
\r
770 #define JAWS_DELETE(X) X
\r
771 #define SAYMACHINEMOVE()
\r
775 /*---------------------------------------------------------------------------*\
\r
779 \*---------------------------------------------------------------------------*/
\r
782 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
783 LPSTR lpCmdLine, int nCmdShow)
\r
786 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
787 // INITCOMMONCONTROLSEX ex;
\r
791 LoadLibrary("RICHED32.DLL");
\r
792 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
794 if (!InitApplication(hInstance)) {
\r
797 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
804 // InitCommonControlsEx(&ex);
\r
805 InitCommonControls();
\r
807 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
808 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
809 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
811 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
813 while (GetMessage(&msg, /* message structure */
\r
814 NULL, /* handle of window receiving the message */
\r
815 0, /* lowest message to examine */
\r
816 0)) /* highest message to examine */
\r
819 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
820 // [HGM] navigate: switch between all windows with tab
\r
821 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
822 int i, currentElement = 0;
\r
824 // first determine what element of the chain we come from (if any)
\r
825 if(appData.icsActive) {
\r
826 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
827 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
829 if(engineOutputDialog && EngineOutputIsUp()) {
\r
830 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
831 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
833 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
834 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
836 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
837 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
838 if(msg.hwnd == e1) currentElement = 2; else
\r
839 if(msg.hwnd == e2) currentElement = 3; else
\r
840 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
841 if(msg.hwnd == mh) currentElement = 4; else
\r
842 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
843 if(msg.hwnd == hText) currentElement = 5; else
\r
844 if(msg.hwnd == hInput) currentElement = 6; else
\r
845 for (i = 0; i < N_BUTTONS; i++) {
\r
846 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
849 // determine where to go to
\r
850 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
852 currentElement = (currentElement + direction) % 7;
\r
853 switch(currentElement) {
\r
855 h = hwndMain; break; // passing this case always makes the loop exit
\r
857 h = buttonDesc[0].hwnd; break; // could be NULL
\r
859 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
862 if(!EngineOutputIsUp()) continue;
\r
865 if(!MoveHistoryIsUp()) continue;
\r
867 // case 6: // input to eval graph does not seem to get here!
\r
868 // if(!EvalGraphIsUp()) continue;
\r
869 // h = evalGraphDialog; break;
\r
871 if(!appData.icsActive) continue;
\r
875 if(!appData.icsActive) continue;
\r
881 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
882 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
885 continue; // this message now has been processed
\r
889 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
890 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
891 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
892 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
893 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
894 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
895 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
896 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
897 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
898 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
899 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
900 for(i=0; i<MAX_CHAT; i++)
\r
901 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
904 if(done) continue; // [HGM] chat: end patch
\r
905 TranslateMessage(&msg); /* Translates virtual key codes */
\r
906 DispatchMessage(&msg); /* Dispatches message to window */
\r
911 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
914 /*---------------------------------------------------------------------------*\
\r
916 * Initialization functions
\r
918 \*---------------------------------------------------------------------------*/
\r
922 { // update user logo if necessary
\r
923 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
925 if(appData.autoLogo) {
\r
926 curName = UserName();
\r
927 if(strcmp(curName, oldUserName)) {
\r
928 GetCurrentDirectory(MSG_SIZ, dir);
\r
929 SetCurrentDirectory(installDir);
\r
930 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
931 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
932 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
933 if(userLogo == NULL)
\r
934 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
935 SetCurrentDirectory(dir); /* return to prev directory */
\r
941 InitApplication(HINSTANCE hInstance)
\r
945 /* Fill in window class structure with parameters that describe the */
\r
948 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
949 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
950 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
951 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
952 wc.hInstance = hInstance; /* Owner of this class */
\r
953 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
954 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
955 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
956 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
957 wc.lpszClassName = szAppName; /* Name to register as */
\r
959 /* Register the window class and return success/failure code. */
\r
960 if (!RegisterClass(&wc)) return FALSE;
\r
962 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
963 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
965 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
966 wc.hInstance = hInstance;
\r
967 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
968 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
969 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
970 wc.lpszMenuName = NULL;
\r
971 wc.lpszClassName = szConsoleName;
\r
973 if (!RegisterClass(&wc)) return FALSE;
\r
978 /* Set by InitInstance, used by EnsureOnScreen */
\r
979 int screenHeight, screenWidth;
\r
982 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
984 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
985 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
986 if (*x > screenWidth - 32) *x = 0;
\r
987 if (*y > screenHeight - 32) *y = 0;
\r
988 if (*x < minX) *x = minX;
\r
989 if (*y < minY) *y = minY;
\r
993 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
995 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
996 GetCurrentDirectory(MSG_SIZ, dir);
\r
997 SetCurrentDirectory(installDir);
\r
998 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
999 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1001 if (cps->programLogo == NULL && appData.debugMode) {
\r
1002 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1004 } else if(appData.autoLogo) {
\r
1005 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1006 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1007 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1009 if(appData.directory[n] && appData.directory[n][0]) {
\r
1010 SetCurrentDirectory(appData.directory[n]);
\r
1011 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1014 SetCurrentDirectory(dir); /* return to prev directory */
\r
1020 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1021 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1023 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1024 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1025 liteBackTextureMode = appData.liteBackTextureMode;
\r
1027 if (liteBackTexture == NULL && appData.debugMode) {
\r
1028 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1032 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1033 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1034 darkBackTextureMode = appData.darkBackTextureMode;
\r
1036 if (darkBackTexture == NULL && appData.debugMode) {
\r
1037 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1043 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1045 HWND hwnd; /* Main window handle. */
\r
1047 WINDOWPLACEMENT wp;
\r
1050 hInst = hInstance; /* Store instance handle in our global variable */
\r
1051 programName = szAppName;
\r
1053 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1054 *filepart = NULLCHAR;
\r
1056 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1058 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1059 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1060 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1061 /* xboard, and older WinBoards, controlled the move sound with the
\r
1062 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1063 always turn the option on (so that the backend will call us),
\r
1064 then let the user turn the sound off by setting it to silence if
\r
1065 desired. To accommodate old winboard.ini files saved by old
\r
1066 versions of WinBoard, we also turn off the sound if the option
\r
1067 was initially set to false. [HGM] taken out of InitAppData */
\r
1068 if (!appData.ringBellAfterMoves) {
\r
1069 sounds[(int)SoundMove].name = strdup("");
\r
1070 appData.ringBellAfterMoves = TRUE;
\r
1072 if (appData.debugMode) {
\r
1073 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1074 setbuf(debugFP, NULL);
\r
1077 LoadLanguageFile(appData.language);
\r
1081 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1082 // InitEngineUCI( installDir, &second );
\r
1084 /* Create a main window for this application instance. */
\r
1085 hwnd = CreateWindow(szAppName, szTitle,
\r
1086 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1087 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1088 NULL, NULL, hInstance, NULL);
\r
1091 /* If window could not be created, return "failure" */
\r
1096 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1097 LoadLogo(&first, 0, FALSE);
\r
1098 LoadLogo(&second, 1, appData.icsActive);
\r
1102 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1103 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1104 iconCurrent = iconWhite;
\r
1105 InitDrawingColors();
\r
1106 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1107 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1108 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1109 /* Compute window size for each board size, and use the largest
\r
1110 size that fits on this screen as the default. */
\r
1111 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1112 if (boardSize == (BoardSize)-1 &&
\r
1113 winH <= screenHeight
\r
1114 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1115 && winW <= screenWidth) {
\r
1116 boardSize = (BoardSize)ibs;
\r
1120 InitDrawingSizes(boardSize, 0);
\r
1121 RecentEngineMenu(appData.recentEngineList);
\r
1123 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1125 /* [AS] Load textures if specified */
\r
1128 mysrandom( (unsigned) time(NULL) );
\r
1130 /* [AS] Restore layout */
\r
1131 if( wpMoveHistory.visible ) {
\r
1132 MoveHistoryPopUp();
\r
1135 if( wpEvalGraph.visible ) {
\r
1139 if( wpEngineOutput.visible ) {
\r
1140 EngineOutputPopUp();
\r
1143 /* Make the window visible; update its client area; and return "success" */
\r
1144 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1145 wp.length = sizeof(WINDOWPLACEMENT);
\r
1147 wp.showCmd = nCmdShow;
\r
1148 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1149 wp.rcNormalPosition.left = wpMain.x;
\r
1150 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1151 wp.rcNormalPosition.top = wpMain.y;
\r
1152 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1153 SetWindowPlacement(hwndMain, &wp);
\r
1155 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1157 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1158 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1160 if (hwndConsole) {
\r
1162 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1163 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1165 ShowWindow(hwndConsole, nCmdShow);
\r
1166 SetActiveWindow(hwndConsole);
\r
1168 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1169 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1178 HMENU hmenu = GetMenu(hwndMain);
\r
1180 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1181 MF_BYCOMMAND|((appData.icsActive &&
\r
1182 *appData.icsCommPort != NULLCHAR) ?
\r
1183 MF_ENABLED : MF_GRAYED));
\r
1184 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1185 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1186 MF_CHECKED : MF_UNCHECKED));
\r
1189 //---------------------------------------------------------------------------------------------------------
\r
1191 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1192 #define XBOARD FALSE
\r
1194 #define OPTCHAR "/"
\r
1195 #define SEPCHAR "="
\r
1199 // front-end part of option handling
\r
1202 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1204 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1205 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1208 lf->lfEscapement = 0;
\r
1209 lf->lfOrientation = 0;
\r
1210 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1211 lf->lfItalic = mfp->italic;
\r
1212 lf->lfUnderline = mfp->underline;
\r
1213 lf->lfStrikeOut = mfp->strikeout;
\r
1214 lf->lfCharSet = mfp->charset;
\r
1215 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1216 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1217 lf->lfQuality = DEFAULT_QUALITY;
\r
1218 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1219 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1223 CreateFontInMF(MyFont *mf)
\r
1225 LFfromMFP(&mf->lf, &mf->mfp);
\r
1226 if (mf->hf) DeleteObject(mf->hf);
\r
1227 mf->hf = CreateFontIndirect(&mf->lf);
\r
1230 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1232 colorVariable[] = {
\r
1233 &whitePieceColor,
\r
1234 &blackPieceColor,
\r
1235 &lightSquareColor,
\r
1236 &darkSquareColor,
\r
1237 &highlightSquareColor,
\r
1238 &premoveHighlightColor,
\r
1240 &consoleBackgroundColor,
\r
1241 &appData.fontForeColorWhite,
\r
1242 &appData.fontBackColorWhite,
\r
1243 &appData.fontForeColorBlack,
\r
1244 &appData.fontBackColorBlack,
\r
1245 &appData.evalHistColorWhite,
\r
1246 &appData.evalHistColorBlack,
\r
1247 &appData.highlightArrowColor,
\r
1250 /* Command line font name parser. NULL name means do nothing.
\r
1251 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1252 For backward compatibility, syntax without the colon is also
\r
1253 accepted, but font names with digits in them won't work in that case.
\r
1256 ParseFontName(char *name, MyFontParams *mfp)
\r
1259 if (name == NULL) return;
\r
1261 q = strchr(p, ':');
\r
1263 if (q - p >= sizeof(mfp->faceName))
\r
1264 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1265 memcpy(mfp->faceName, p, q - p);
\r
1266 mfp->faceName[q - p] = NULLCHAR;
\r
1269 q = mfp->faceName;
\r
1270 while (*p && !isdigit(*p)) {
\r
1272 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1273 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1275 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1278 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1279 mfp->pointSize = (float) atof(p);
\r
1280 mfp->bold = (strchr(p, 'b') != NULL);
\r
1281 mfp->italic = (strchr(p, 'i') != NULL);
\r
1282 mfp->underline = (strchr(p, 'u') != NULL);
\r
1283 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1284 mfp->charset = DEFAULT_CHARSET;
\r
1285 q = strchr(p, 'c');
\r
1287 mfp->charset = (BYTE) atoi(q+1);
\r
1291 ParseFont(char *name, int number)
\r
1292 { // wrapper to shield back-end from 'font'
\r
1293 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1298 { // in WB we have a 2D array of fonts; this initializes their description
\r
1300 /* Point font array elements to structures and
\r
1301 parse default font names */
\r
1302 for (i=0; i<NUM_FONTS; i++) {
\r
1303 for (j=0; j<NUM_SIZES; j++) {
\r
1304 font[j][i] = &fontRec[j][i];
\r
1305 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1312 { // here we create the actual fonts from the selected descriptions
\r
1314 for (i=0; i<NUM_FONTS; i++) {
\r
1315 for (j=0; j<NUM_SIZES; j++) {
\r
1316 CreateFontInMF(font[j][i]);
\r
1320 /* Color name parser.
\r
1321 X version accepts X color names, but this one
\r
1322 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1324 ParseColorName(char *name)
\r
1326 int red, green, blue, count;
\r
1327 char buf[MSG_SIZ];
\r
1329 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1331 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1332 &red, &green, &blue);
\r
1335 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1336 DisplayError(buf, 0);
\r
1337 return RGB(0, 0, 0);
\r
1339 return PALETTERGB(red, green, blue);
\r
1343 ParseColor(int n, char *name)
\r
1344 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1345 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1349 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1351 char *e = argValue;
\r
1355 if (*e == 'b') eff |= CFE_BOLD;
\r
1356 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1357 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1358 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1359 else if (*e == '#' || isdigit(*e)) break;
\r
1363 *color = ParseColorName(e);
\r
1367 ParseTextAttribs(ColorClass cc, char *s)
\r
1368 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1369 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1370 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1374 ParseBoardSize(void *addr, char *name)
\r
1375 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1376 BoardSize bs = SizeTiny;
\r
1377 while (sizeInfo[bs].name != NULL) {
\r
1378 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1379 *(BoardSize *)addr = bs;
\r
1384 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1389 { // [HGM] import name from appData first
\r
1392 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1393 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1394 textAttribs[cc].sound.data = NULL;
\r
1395 MyLoadSound(&textAttribs[cc].sound);
\r
1397 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1398 textAttribs[cc].sound.name = strdup("");
\r
1399 textAttribs[cc].sound.data = NULL;
\r
1401 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1402 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1403 sounds[sc].data = NULL;
\r
1404 MyLoadSound(&sounds[sc]);
\r
1409 SetCommPortDefaults()
\r
1411 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1412 dcb.DCBlength = sizeof(DCB);
\r
1413 dcb.BaudRate = 9600;
\r
1414 dcb.fBinary = TRUE;
\r
1415 dcb.fParity = FALSE;
\r
1416 dcb.fOutxCtsFlow = FALSE;
\r
1417 dcb.fOutxDsrFlow = FALSE;
\r
1418 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1419 dcb.fDsrSensitivity = FALSE;
\r
1420 dcb.fTXContinueOnXoff = TRUE;
\r
1421 dcb.fOutX = FALSE;
\r
1423 dcb.fNull = FALSE;
\r
1424 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1425 dcb.fAbortOnError = FALSE;
\r
1427 dcb.Parity = SPACEPARITY;
\r
1428 dcb.StopBits = ONESTOPBIT;
\r
1431 // [HGM] args: these three cases taken out to stay in front-end
\r
1433 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1434 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1435 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1436 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1438 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1439 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1440 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1441 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1442 ad->argName, mfp->faceName, mfp->pointSize,
\r
1443 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1444 mfp->bold ? "b" : "",
\r
1445 mfp->italic ? "i" : "",
\r
1446 mfp->underline ? "u" : "",
\r
1447 mfp->strikeout ? "s" : "",
\r
1448 (int)mfp->charset);
\r
1454 { // [HGM] copy the names from the internal WB variables to appData
\r
1457 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1458 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1459 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1460 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1464 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1465 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1466 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1467 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1468 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1469 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1470 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1471 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1472 (ta->effects) ? " " : "",
\r
1473 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1477 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1478 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1479 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1480 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1481 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1485 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1486 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1487 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1491 ParseCommPortSettings(char *s)
\r
1492 { // wrapper to keep dcb from back-end
\r
1493 ParseCommSettings(s, &dcb);
\r
1498 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1499 GetActualPlacement(hwndMain, &wpMain);
\r
1500 GetActualPlacement(hwndConsole, &wpConsole);
\r
1501 GetActualPlacement(commentDialog, &wpComment);
\r
1502 GetActualPlacement(editTagsDialog, &wpTags);
\r
1503 GetActualPlacement(gameListDialog, &wpGameList);
\r
1504 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1505 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1506 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1510 PrintCommPortSettings(FILE *f, char *name)
\r
1511 { // wrapper to shield back-end from DCB
\r
1512 PrintCommSettings(f, name, &dcb);
\r
1516 MySearchPath(char *installDir, char *name, char *fullname)
\r
1518 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1519 if(name[0]== '%') {
\r
1520 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1521 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1522 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1523 *strchr(buf, '%') = 0;
\r
1524 strcat(fullname, getenv(buf));
\r
1525 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1527 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1528 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1529 return (int) strlen(fullname);
\r
1531 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1535 MyGetFullPathName(char *name, char *fullname)
\r
1538 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1543 { // [HGM] args: allows testing if main window is realized from back-end
\r
1544 return hwndMain != NULL;
\r
1548 PopUpStartupDialog()
\r
1552 LoadLanguageFile(appData.language);
\r
1553 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1554 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1555 FreeProcInstance(lpProc);
\r
1558 /*---------------------------------------------------------------------------*\
\r
1560 * GDI board drawing routines
\r
1562 \*---------------------------------------------------------------------------*/
\r
1564 /* [AS] Draw square using background texture */
\r
1565 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1570 return; /* Should never happen! */
\r
1573 SetGraphicsMode( dst, GM_ADVANCED );
\r
1580 /* X reflection */
\r
1585 x.eDx = (FLOAT) dw + dx - 1;
\r
1588 SetWorldTransform( dst, &x );
\r
1591 /* Y reflection */
\r
1597 x.eDy = (FLOAT) dh + dy - 1;
\r
1599 SetWorldTransform( dst, &x );
\r
1607 x.eDx = (FLOAT) dx;
\r
1608 x.eDy = (FLOAT) dy;
\r
1611 SetWorldTransform( dst, &x );
\r
1615 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1623 SetWorldTransform( dst, &x );
\r
1625 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1628 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1630 PM_WP = (int) WhitePawn,
\r
1631 PM_WN = (int) WhiteKnight,
\r
1632 PM_WB = (int) WhiteBishop,
\r
1633 PM_WR = (int) WhiteRook,
\r
1634 PM_WQ = (int) WhiteQueen,
\r
1635 PM_WF = (int) WhiteFerz,
\r
1636 PM_WW = (int) WhiteWazir,
\r
1637 PM_WE = (int) WhiteAlfil,
\r
1638 PM_WM = (int) WhiteMan,
\r
1639 PM_WO = (int) WhiteCannon,
\r
1640 PM_WU = (int) WhiteUnicorn,
\r
1641 PM_WH = (int) WhiteNightrider,
\r
1642 PM_WA = (int) WhiteAngel,
\r
1643 PM_WC = (int) WhiteMarshall,
\r
1644 PM_WAB = (int) WhiteCardinal,
\r
1645 PM_WD = (int) WhiteDragon,
\r
1646 PM_WL = (int) WhiteLance,
\r
1647 PM_WS = (int) WhiteCobra,
\r
1648 PM_WV = (int) WhiteFalcon,
\r
1649 PM_WSG = (int) WhiteSilver,
\r
1650 PM_WG = (int) WhiteGrasshopper,
\r
1651 PM_WK = (int) WhiteKing,
\r
1652 PM_BP = (int) BlackPawn,
\r
1653 PM_BN = (int) BlackKnight,
\r
1654 PM_BB = (int) BlackBishop,
\r
1655 PM_BR = (int) BlackRook,
\r
1656 PM_BQ = (int) BlackQueen,
\r
1657 PM_BF = (int) BlackFerz,
\r
1658 PM_BW = (int) BlackWazir,
\r
1659 PM_BE = (int) BlackAlfil,
\r
1660 PM_BM = (int) BlackMan,
\r
1661 PM_BO = (int) BlackCannon,
\r
1662 PM_BU = (int) BlackUnicorn,
\r
1663 PM_BH = (int) BlackNightrider,
\r
1664 PM_BA = (int) BlackAngel,
\r
1665 PM_BC = (int) BlackMarshall,
\r
1666 PM_BG = (int) BlackGrasshopper,
\r
1667 PM_BAB = (int) BlackCardinal,
\r
1668 PM_BD = (int) BlackDragon,
\r
1669 PM_BL = (int) BlackLance,
\r
1670 PM_BS = (int) BlackCobra,
\r
1671 PM_BV = (int) BlackFalcon,
\r
1672 PM_BSG = (int) BlackSilver,
\r
1673 PM_BK = (int) BlackKing
\r
1676 static HFONT hPieceFont = NULL;
\r
1677 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1678 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1679 static int fontBitmapSquareSize = 0;
\r
1680 static char pieceToFontChar[(int) EmptySquare] =
\r
1681 { 'p', 'n', 'b', 'r', 'q',
\r
1682 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1683 'k', 'o', 'm', 'v', 't', 'w',
\r
1684 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1687 extern BOOL SetCharTable( char *table, const char * map );
\r
1688 /* [HGM] moved to backend.c */
\r
1690 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1693 BYTE r1 = GetRValue( color );
\r
1694 BYTE g1 = GetGValue( color );
\r
1695 BYTE b1 = GetBValue( color );
\r
1701 /* Create a uniform background first */
\r
1702 hbrush = CreateSolidBrush( color );
\r
1703 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1704 FillRect( hdc, &rc, hbrush );
\r
1705 DeleteObject( hbrush );
\r
1708 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1709 int steps = squareSize / 2;
\r
1712 for( i=0; i<steps; i++ ) {
\r
1713 BYTE r = r1 - (r1-r2) * i / steps;
\r
1714 BYTE g = g1 - (g1-g2) * i / steps;
\r
1715 BYTE b = b1 - (b1-b2) * i / steps;
\r
1717 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1718 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1719 FillRect( hdc, &rc, hbrush );
\r
1720 DeleteObject(hbrush);
\r
1723 else if( mode == 2 ) {
\r
1724 /* Diagonal gradient, good more or less for every piece */
\r
1725 POINT triangle[3];
\r
1726 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1727 HBRUSH hbrush_old;
\r
1728 int steps = squareSize;
\r
1731 triangle[0].x = squareSize - steps;
\r
1732 triangle[0].y = squareSize;
\r
1733 triangle[1].x = squareSize;
\r
1734 triangle[1].y = squareSize;
\r
1735 triangle[2].x = squareSize;
\r
1736 triangle[2].y = squareSize - steps;
\r
1738 for( i=0; i<steps; i++ ) {
\r
1739 BYTE r = r1 - (r1-r2) * i / steps;
\r
1740 BYTE g = g1 - (g1-g2) * i / steps;
\r
1741 BYTE b = b1 - (b1-b2) * i / steps;
\r
1743 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1744 hbrush_old = SelectObject( hdc, hbrush );
\r
1745 Polygon( hdc, triangle, 3 );
\r
1746 SelectObject( hdc, hbrush_old );
\r
1747 DeleteObject(hbrush);
\r
1752 SelectObject( hdc, hpen );
\r
1757 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1758 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1759 piece: follow the steps as explained below.
\r
1761 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1765 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1769 int backColor = whitePieceColor;
\r
1770 int foreColor = blackPieceColor;
\r
1772 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1773 backColor = appData.fontBackColorWhite;
\r
1774 foreColor = appData.fontForeColorWhite;
\r
1776 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1777 backColor = appData.fontBackColorBlack;
\r
1778 foreColor = appData.fontForeColorBlack;
\r
1782 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1784 hbm_old = SelectObject( hdc, hbm );
\r
1788 rc.right = squareSize;
\r
1789 rc.bottom = squareSize;
\r
1791 /* Step 1: background is now black */
\r
1792 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1794 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1796 pt.x = (squareSize - sz.cx) / 2;
\r
1797 pt.y = (squareSize - sz.cy) / 2;
\r
1799 SetBkMode( hdc, TRANSPARENT );
\r
1800 SetTextColor( hdc, chroma );
\r
1801 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1802 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1804 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1805 /* Step 3: the area outside the piece is filled with white */
\r
1806 // FloodFill( hdc, 0, 0, chroma );
\r
1807 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1808 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1809 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1810 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1811 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1813 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1814 but if the start point is not inside the piece we're lost!
\r
1815 There should be a better way to do this... if we could create a region or path
\r
1816 from the fill operation we would be fine for example.
\r
1818 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1819 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1821 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1822 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1823 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1825 SelectObject( dc2, bm2 );
\r
1826 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1827 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1828 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1829 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1830 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1833 DeleteObject( bm2 );
\r
1836 SetTextColor( hdc, 0 );
\r
1838 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1839 draw the piece again in black for safety.
\r
1841 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1843 SelectObject( hdc, hbm_old );
\r
1845 if( hPieceMask[index] != NULL ) {
\r
1846 DeleteObject( hPieceMask[index] );
\r
1849 hPieceMask[index] = hbm;
\r
1852 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1854 SelectObject( hdc, hbm );
\r
1857 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1858 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1859 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1861 SelectObject( dc1, hPieceMask[index] );
\r
1862 SelectObject( dc2, bm2 );
\r
1863 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1864 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1867 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1868 the piece background and deletes (makes transparent) the rest.
\r
1869 Thanks to that mask, we are free to paint the background with the greates
\r
1870 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1871 We use this, to make gradients and give the pieces a "roundish" look.
\r
1873 SetPieceBackground( hdc, backColor, 2 );
\r
1874 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1878 DeleteObject( bm2 );
\r
1881 SetTextColor( hdc, foreColor );
\r
1882 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1884 SelectObject( hdc, hbm_old );
\r
1886 if( hPieceFace[index] != NULL ) {
\r
1887 DeleteObject( hPieceFace[index] );
\r
1890 hPieceFace[index] = hbm;
\r
1893 static int TranslatePieceToFontPiece( int piece )
\r
1923 case BlackMarshall:
\r
1927 case BlackNightrider:
\r
1933 case BlackUnicorn:
\r
1937 case BlackGrasshopper:
\r
1949 case BlackCardinal:
\r
1956 case WhiteMarshall:
\r
1960 case WhiteNightrider:
\r
1966 case WhiteUnicorn:
\r
1970 case WhiteGrasshopper:
\r
1982 case WhiteCardinal:
\r
1991 void CreatePiecesFromFont()
\r
1994 HDC hdc_window = NULL;
\r
2000 if( fontBitmapSquareSize < 0 ) {
\r
2001 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2005 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2006 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2007 fontBitmapSquareSize = -1;
\r
2011 if( fontBitmapSquareSize != squareSize ) {
\r
2012 hdc_window = GetDC( hwndMain );
\r
2013 hdc = CreateCompatibleDC( hdc_window );
\r
2015 if( hPieceFont != NULL ) {
\r
2016 DeleteObject( hPieceFont );
\r
2019 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2020 hPieceMask[i] = NULL;
\r
2021 hPieceFace[i] = NULL;
\r
2027 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2028 fontHeight = appData.fontPieceSize;
\r
2031 fontHeight = (fontHeight * squareSize) / 100;
\r
2033 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2035 lf.lfEscapement = 0;
\r
2036 lf.lfOrientation = 0;
\r
2037 lf.lfWeight = FW_NORMAL;
\r
2039 lf.lfUnderline = 0;
\r
2040 lf.lfStrikeOut = 0;
\r
2041 lf.lfCharSet = DEFAULT_CHARSET;
\r
2042 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2043 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2044 lf.lfQuality = PROOF_QUALITY;
\r
2045 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2046 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2047 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2049 hPieceFont = CreateFontIndirect( &lf );
\r
2051 if( hPieceFont == NULL ) {
\r
2052 fontBitmapSquareSize = -2;
\r
2055 /* Setup font-to-piece character table */
\r
2056 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2057 /* No (or wrong) global settings, try to detect the font */
\r
2058 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2060 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2062 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2063 /* DiagramTT* family */
\r
2064 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2066 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2067 /* Fairy symbols */
\r
2068 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2070 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2071 /* Good Companion (Some characters get warped as literal :-( */
\r
2072 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2073 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2074 SetCharTable(pieceToFontChar, s);
\r
2077 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2078 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2082 /* Create bitmaps */
\r
2083 hfont_old = SelectObject( hdc, hPieceFont );
\r
2084 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2085 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2086 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2088 SelectObject( hdc, hfont_old );
\r
2090 fontBitmapSquareSize = squareSize;
\r
2094 if( hdc != NULL ) {
\r
2098 if( hdc_window != NULL ) {
\r
2099 ReleaseDC( hwndMain, hdc_window );
\r
2104 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2108 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2109 if (gameInfo.event &&
\r
2110 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2111 strcmp(name, "k80s") == 0) {
\r
2112 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2114 return LoadBitmap(hinst, name);
\r
2118 /* Insert a color into the program's logical palette
\r
2119 structure. This code assumes the given color is
\r
2120 the result of the RGB or PALETTERGB macro, and it
\r
2121 knows how those macros work (which is documented).
\r
2124 InsertInPalette(COLORREF color)
\r
2126 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2128 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2129 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2130 pLogPal->palNumEntries--;
\r
2134 pe->peFlags = (char) 0;
\r
2135 pe->peRed = (char) (0xFF & color);
\r
2136 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2137 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2143 InitDrawingColors()
\r
2145 if (pLogPal == NULL) {
\r
2146 /* Allocate enough memory for a logical palette with
\r
2147 * PALETTESIZE entries and set the size and version fields
\r
2148 * of the logical palette structure.
\r
2150 pLogPal = (NPLOGPALETTE)
\r
2151 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2152 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2153 pLogPal->palVersion = 0x300;
\r
2155 pLogPal->palNumEntries = 0;
\r
2157 InsertInPalette(lightSquareColor);
\r
2158 InsertInPalette(darkSquareColor);
\r
2159 InsertInPalette(whitePieceColor);
\r
2160 InsertInPalette(blackPieceColor);
\r
2161 InsertInPalette(highlightSquareColor);
\r
2162 InsertInPalette(premoveHighlightColor);
\r
2164 /* create a logical color palette according the information
\r
2165 * in the LOGPALETTE structure.
\r
2167 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2169 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2170 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2171 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2172 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2173 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2174 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2175 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2176 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2177 /* [AS] Force rendering of the font-based pieces */
\r
2178 if( fontBitmapSquareSize > 0 ) {
\r
2179 fontBitmapSquareSize = 0;
\r
2185 BoardWidth(int boardSize, int n)
\r
2186 { /* [HGM] argument n added to allow different width and height */
\r
2187 int lineGap = sizeInfo[boardSize].lineGap;
\r
2189 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2190 lineGap = appData.overrideLineGap;
\r
2193 return (n + 1) * lineGap +
\r
2194 n * sizeInfo[boardSize].squareSize;
\r
2197 /* Respond to board resize by dragging edge */
\r
2199 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2201 BoardSize newSize = NUM_SIZES - 1;
\r
2202 static int recurse = 0;
\r
2203 if (IsIconic(hwndMain)) return;
\r
2204 if (recurse > 0) return;
\r
2206 while (newSize > 0) {
\r
2207 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2208 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2209 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2212 boardSize = newSize;
\r
2213 InitDrawingSizes(boardSize, flags);
\r
2218 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2221 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2223 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2224 ChessSquare piece;
\r
2225 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2227 SIZE clockSize, messageSize;
\r
2229 char buf[MSG_SIZ];
\r
2231 HMENU hmenu = GetMenu(hwndMain);
\r
2232 RECT crect, wrect, oldRect;
\r
2234 LOGBRUSH logbrush;
\r
2236 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2237 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2239 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2240 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2242 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2243 oldRect.top = wpMain.y;
\r
2244 oldRect.right = wpMain.x + wpMain.width;
\r
2245 oldRect.bottom = wpMain.y + wpMain.height;
\r
2247 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2248 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2249 squareSize = sizeInfo[boardSize].squareSize;
\r
2250 lineGap = sizeInfo[boardSize].lineGap;
\r
2251 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2253 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2254 lineGap = appData.overrideLineGap;
\r
2257 if (tinyLayout != oldTinyLayout) {
\r
2258 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2260 style &= ~WS_SYSMENU;
\r
2261 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2262 "&Minimize\tCtrl+F4");
\r
2264 style |= WS_SYSMENU;
\r
2265 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2267 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2269 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2270 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2271 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2273 DrawMenuBar(hwndMain);
\r
2276 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2277 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2279 /* Get text area sizes */
\r
2280 hdc = GetDC(hwndMain);
\r
2281 if (appData.clockMode) {
\r
2282 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2284 snprintf(buf, MSG_SIZ, _("White"));
\r
2286 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2287 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2288 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2289 str = _("We only care about the height here");
\r
2290 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2291 SelectObject(hdc, oldFont);
\r
2292 ReleaseDC(hwndMain, hdc);
\r
2294 /* Compute where everything goes */
\r
2295 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2296 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2297 logoHeight = 2*clockSize.cy;
\r
2298 leftLogoRect.left = OUTER_MARGIN;
\r
2299 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2300 leftLogoRect.top = OUTER_MARGIN;
\r
2301 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2303 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2304 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2305 rightLogoRect.top = OUTER_MARGIN;
\r
2306 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2309 whiteRect.left = leftLogoRect.right;
\r
2310 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2311 whiteRect.top = OUTER_MARGIN;
\r
2312 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2314 blackRect.right = rightLogoRect.left;
\r
2315 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2316 blackRect.top = whiteRect.top;
\r
2317 blackRect.bottom = whiteRect.bottom;
\r
2319 whiteRect.left = OUTER_MARGIN;
\r
2320 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2321 whiteRect.top = OUTER_MARGIN;
\r
2322 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2324 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2325 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2326 blackRect.top = whiteRect.top;
\r
2327 blackRect.bottom = whiteRect.bottom;
\r
2329 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2332 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2333 if (appData.showButtonBar) {
\r
2334 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2335 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2337 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2339 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2340 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2342 boardRect.left = OUTER_MARGIN;
\r
2343 boardRect.right = boardRect.left + boardWidth;
\r
2344 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2345 boardRect.bottom = boardRect.top + boardHeight;
\r
2347 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2348 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2349 oldBoardSize = boardSize;
\r
2350 oldTinyLayout = tinyLayout;
\r
2351 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2352 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2353 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2354 winW *= 1 + twoBoards;
\r
2355 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2356 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2357 wpMain.height = winH; // without disturbing window attachments
\r
2358 GetWindowRect(hwndMain, &wrect);
\r
2359 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2360 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2362 // [HGM] placement: let attached windows follow size change.
\r
2363 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2364 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2365 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2366 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2367 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2369 /* compensate if menu bar wrapped */
\r
2370 GetClientRect(hwndMain, &crect);
\r
2371 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2372 wpMain.height += offby;
\r
2374 case WMSZ_TOPLEFT:
\r
2375 SetWindowPos(hwndMain, NULL,
\r
2376 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2377 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2380 case WMSZ_TOPRIGHT:
\r
2382 SetWindowPos(hwndMain, NULL,
\r
2383 wrect.left, wrect.bottom - wpMain.height,
\r
2384 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2387 case WMSZ_BOTTOMLEFT:
\r
2389 SetWindowPos(hwndMain, NULL,
\r
2390 wrect.right - wpMain.width, wrect.top,
\r
2391 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2394 case WMSZ_BOTTOMRIGHT:
\r
2398 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2399 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2404 for (i = 0; i < N_BUTTONS; i++) {
\r
2405 if (buttonDesc[i].hwnd != NULL) {
\r
2406 DestroyWindow(buttonDesc[i].hwnd);
\r
2407 buttonDesc[i].hwnd = NULL;
\r
2409 if (appData.showButtonBar) {
\r
2410 buttonDesc[i].hwnd =
\r
2411 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2412 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2413 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2414 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2415 (HMENU) buttonDesc[i].id,
\r
2416 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2418 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2419 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2420 MAKELPARAM(FALSE, 0));
\r
2422 if (buttonDesc[i].id == IDM_Pause)
\r
2423 hwndPause = buttonDesc[i].hwnd;
\r
2424 buttonDesc[i].wndproc = (WNDPROC)
\r
2425 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2428 if (gridPen != NULL) DeleteObject(gridPen);
\r
2429 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2430 if (premovePen != NULL) DeleteObject(premovePen);
\r
2431 if (lineGap != 0) {
\r
2432 logbrush.lbStyle = BS_SOLID;
\r
2433 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2435 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2436 lineGap, &logbrush, 0, NULL);
\r
2437 logbrush.lbColor = highlightSquareColor;
\r
2439 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2440 lineGap, &logbrush, 0, NULL);
\r
2442 logbrush.lbColor = premoveHighlightColor;
\r
2444 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2445 lineGap, &logbrush, 0, NULL);
\r
2447 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2448 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2449 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2450 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2451 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2452 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2453 BOARD_WIDTH * (squareSize + lineGap);
\r
2454 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2456 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2457 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2458 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2459 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2460 lineGap / 2 + (i * (squareSize + lineGap));
\r
2461 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2462 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2463 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2467 /* [HGM] Licensing requirement */
\r
2469 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2472 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2474 GothicPopUp( "", VariantNormal);
\r
2477 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2479 /* Load piece bitmaps for this board size */
\r
2480 for (i=0; i<=2; i++) {
\r
2481 for (piece = WhitePawn;
\r
2482 (int) piece < (int) BlackPawn;
\r
2483 piece = (ChessSquare) ((int) piece + 1)) {
\r
2484 if (pieceBitmap[i][piece] != NULL)
\r
2485 DeleteObject(pieceBitmap[i][piece]);
\r
2489 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2490 // Orthodox Chess pieces
\r
2491 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2492 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2493 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2494 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2495 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2496 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2497 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2498 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2499 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2500 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2501 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2502 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2503 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2504 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2505 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2506 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2507 // in Shogi, Hijack the unused Queen for Lance
\r
2508 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2512 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2513 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2514 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2517 if(squareSize <= 72 && squareSize >= 33) {
\r
2518 /* A & C are available in most sizes now */
\r
2519 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2520 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2523 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2526 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2527 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2528 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2529 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2530 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2531 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2532 } else { // Smirf-like
\r
2533 if(gameInfo.variant == VariantSChess) {
\r
2534 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2535 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2536 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2538 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2543 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2544 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2547 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2548 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2551 } else { // WinBoard standard
\r
2552 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2553 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2554 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2559 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2560 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2569 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2572 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2573 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2574 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2575 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2576 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2577 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2578 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2579 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2580 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2591 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2592 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2595 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2598 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2601 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2602 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2603 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2605 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2606 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2607 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2608 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2609 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2610 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2611 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2614 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2619 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2620 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2621 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2622 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2623 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2624 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2625 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2626 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2627 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2628 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2629 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2630 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2631 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2632 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2633 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2637 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2638 /* special Shogi support in this size */
\r
2639 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2640 for (piece = WhitePawn;
\r
2641 (int) piece < (int) BlackPawn;
\r
2642 piece = (ChessSquare) ((int) piece + 1)) {
\r
2643 if (pieceBitmap[i][piece] != NULL)
\r
2644 DeleteObject(pieceBitmap[i][piece]);
\r
2647 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2648 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2649 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2650 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2651 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2652 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2653 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2654 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2655 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2656 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2657 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2658 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2659 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2660 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2661 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2662 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2663 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2664 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2665 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2666 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2667 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2668 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2669 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2670 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2671 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2672 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2673 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2674 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2675 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2676 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2677 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2678 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2679 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2680 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2681 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2682 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2683 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2684 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2685 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2686 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2687 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2688 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2694 PieceBitmap(ChessSquare p, int kind)
\r
2696 if ((int) p >= (int) BlackPawn)
\r
2697 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2699 return pieceBitmap[kind][(int) p];
\r
2702 /***************************************************************/
\r
2704 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2705 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2707 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2708 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2712 SquareToPos(int row, int column, int * x, int * y)
\r
2715 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2716 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2718 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2719 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2724 DrawCoordsOnDC(HDC hdc)
\r
2726 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2727 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2728 char str[2] = { NULLCHAR, NULLCHAR };
\r
2729 int oldMode, oldAlign, x, y, start, i;
\r
2733 if (!appData.showCoords)
\r
2736 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2738 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2739 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2740 oldAlign = GetTextAlign(hdc);
\r
2741 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2743 y = boardRect.top + lineGap;
\r
2744 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2746 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2747 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2748 str[0] = files[start + i];
\r
2749 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2750 y += squareSize + lineGap;
\r
2753 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2755 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2756 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2757 str[0] = ranks[start + i];
\r
2758 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2759 x += squareSize + lineGap;
\r
2762 SelectObject(hdc, oldBrush);
\r
2763 SetBkMode(hdc, oldMode);
\r
2764 SetTextAlign(hdc, oldAlign);
\r
2765 SelectObject(hdc, oldFont);
\r
2769 DrawGridOnDC(HDC hdc)
\r
2773 if (lineGap != 0) {
\r
2774 oldPen = SelectObject(hdc, gridPen);
\r
2775 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2776 SelectObject(hdc, oldPen);
\r
2780 #define HIGHLIGHT_PEN 0
\r
2781 #define PREMOVE_PEN 1
\r
2784 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2787 HPEN oldPen, hPen;
\r
2788 if (lineGap == 0) return;
\r
2790 x1 = boardRect.left +
\r
2791 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2792 y1 = boardRect.top +
\r
2793 lineGap/2 + y * (squareSize + lineGap);
\r
2795 x1 = boardRect.left +
\r
2796 lineGap/2 + x * (squareSize + lineGap);
\r
2797 y1 = boardRect.top +
\r
2798 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2800 hPen = pen ? premovePen : highlightPen;
\r
2801 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2802 MoveToEx(hdc, x1, y1, NULL);
\r
2803 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2804 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2805 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2806 LineTo(hdc, x1, y1);
\r
2807 SelectObject(hdc, oldPen);
\r
2811 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2814 for (i=0; i<2; i++) {
\r
2815 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2816 DrawHighlightOnDC(hdc, TRUE,
\r
2817 h->sq[i].x, h->sq[i].y,
\r
2822 /* Note: sqcolor is used only in monoMode */
\r
2823 /* Note that this code is largely duplicated in woptions.c,
\r
2824 function DrawSampleSquare, so that needs to be updated too */
\r
2826 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2828 HBITMAP oldBitmap;
\r
2832 if (appData.blindfold) return;
\r
2834 /* [AS] Use font-based pieces if needed */
\r
2835 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2836 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2837 CreatePiecesFromFont();
\r
2839 if( fontBitmapSquareSize == squareSize ) {
\r
2840 int index = TranslatePieceToFontPiece(piece);
\r
2842 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2844 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2845 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2849 squareSize, squareSize,
\r
2854 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2856 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2857 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2861 squareSize, squareSize,
\r
2870 if (appData.monoMode) {
\r
2871 SelectObject(tmphdc, PieceBitmap(piece,
\r
2872 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2873 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2874 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2876 tmpSize = squareSize;
\r
2878 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2879 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2880 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2881 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2882 x += (squareSize - minorSize)>>1;
\r
2883 y += squareSize - minorSize - 2;
\r
2884 tmpSize = minorSize;
\r
2886 if (color || appData.allWhite ) {
\r
2887 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2889 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2890 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2891 if(appData.upsideDown && color==flipView)
\r
2892 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2894 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2895 /* Use black for outline of white pieces */
\r
2896 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2897 if(appData.upsideDown && color==flipView)
\r
2898 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2900 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2902 /* Use square color for details of black pieces */
\r
2903 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2904 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2905 if(appData.upsideDown && !flipView)
\r
2906 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2908 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2910 SelectObject(hdc, oldBrush);
\r
2911 SelectObject(tmphdc, oldBitmap);
\r
2915 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2916 int GetBackTextureMode( int algo )
\r
2918 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2922 case BACK_TEXTURE_MODE_PLAIN:
\r
2923 result = 1; /* Always use identity map */
\r
2925 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2926 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2934 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2935 to handle redraws cleanly (as random numbers would always be different).
\r
2937 VOID RebuildTextureSquareInfo()
\r
2947 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2949 if( liteBackTexture != NULL ) {
\r
2950 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2951 lite_w = bi.bmWidth;
\r
2952 lite_h = bi.bmHeight;
\r
2956 if( darkBackTexture != NULL ) {
\r
2957 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2958 dark_w = bi.bmWidth;
\r
2959 dark_h = bi.bmHeight;
\r
2963 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2964 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2965 if( (col + row) & 1 ) {
\r
2967 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2968 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2969 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2971 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2972 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2973 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2975 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2976 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2981 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2982 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2983 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2985 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2986 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2987 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2989 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2990 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2997 /* [AS] Arrow highlighting support */
\r
2999 static double A_WIDTH = 5; /* Width of arrow body */
\r
3001 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3002 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3004 static double Sqr( double x )
\r
3009 static int Round( double x )
\r
3011 return (int) (x + 0.5);
\r
3014 /* Draw an arrow between two points using current settings */
\r
3015 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3018 double dx, dy, j, k, x, y;
\r
3020 if( d_x == s_x ) {
\r
3021 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3023 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3026 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3027 arrow[1].y = d_y - h;
\r
3029 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3030 arrow[2].y = d_y - h;
\r
3035 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3036 arrow[5].y = d_y - h;
\r
3038 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3039 arrow[4].y = d_y - h;
\r
3041 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3044 else if( d_y == s_y ) {
\r
3045 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3048 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3050 arrow[1].x = d_x - w;
\r
3051 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3053 arrow[2].x = d_x - w;
\r
3054 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3059 arrow[5].x = d_x - w;
\r
3060 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3062 arrow[4].x = d_x - w;
\r
3063 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3066 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3069 /* [AS] Needed a lot of paper for this! :-) */
\r
3070 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3071 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3073 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3075 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3080 arrow[0].x = Round(x - j);
\r
3081 arrow[0].y = Round(y + j*dx);
\r
3083 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3084 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3087 x = (double) d_x - k;
\r
3088 y = (double) d_y - k*dy;
\r
3091 x = (double) d_x + k;
\r
3092 y = (double) d_y + k*dy;
\r
3095 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3097 arrow[6].x = Round(x - j);
\r
3098 arrow[6].y = Round(y + j*dx);
\r
3100 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3101 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3103 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3104 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3109 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3110 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3113 Polygon( hdc, arrow, 7 );
\r
3116 /* [AS] Draw an arrow between two squares */
\r
3117 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3119 int s_x, s_y, d_x, d_y;
\r
3126 if( s_col == d_col && s_row == d_row ) {
\r
3130 /* Get source and destination points */
\r
3131 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3132 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3135 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3137 else if( d_y < s_y ) {
\r
3138 d_y += squareSize / 2 + squareSize / 4;
\r
3141 d_y += squareSize / 2;
\r
3145 d_x += squareSize / 2 - squareSize / 4;
\r
3147 else if( d_x < s_x ) {
\r
3148 d_x += squareSize / 2 + squareSize / 4;
\r
3151 d_x += squareSize / 2;
\r
3154 s_x += squareSize / 2;
\r
3155 s_y += squareSize / 2;
\r
3157 /* Adjust width */
\r
3158 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3161 stLB.lbStyle = BS_SOLID;
\r
3162 stLB.lbColor = appData.highlightArrowColor;
\r
3165 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3166 holdpen = SelectObject( hdc, hpen );
\r
3167 hbrush = CreateBrushIndirect( &stLB );
\r
3168 holdbrush = SelectObject( hdc, hbrush );
\r
3170 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3172 SelectObject( hdc, holdpen );
\r
3173 SelectObject( hdc, holdbrush );
\r
3174 DeleteObject( hpen );
\r
3175 DeleteObject( hbrush );
\r
3178 BOOL HasHighlightInfo()
\r
3180 BOOL result = FALSE;
\r
3182 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3183 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3191 BOOL IsDrawArrowEnabled()
\r
3193 BOOL result = FALSE;
\r
3195 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3202 VOID DrawArrowHighlight( HDC hdc )
\r
3204 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3205 DrawArrowBetweenSquares( hdc,
\r
3206 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3207 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3211 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3213 HRGN result = NULL;
\r
3215 if( HasHighlightInfo() ) {
\r
3216 int x1, y1, x2, y2;
\r
3217 int sx, sy, dx, dy;
\r
3219 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3220 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3222 sx = MIN( x1, x2 );
\r
3223 sy = MIN( y1, y2 );
\r
3224 dx = MAX( x1, x2 ) + squareSize;
\r
3225 dy = MAX( y1, y2 ) + squareSize;
\r
3227 result = CreateRectRgn( sx, sy, dx, dy );
\r
3234 Warning: this function modifies the behavior of several other functions.
\r
3236 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3237 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3238 repaint is scattered all over the place, which is not good for features such as
\r
3239 "arrow highlighting" that require a full repaint of the board.
\r
3241 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3242 user interaction, when speed is not so important) but especially to avoid errors
\r
3243 in the displayed graphics.
\r
3245 In such patched places, I always try refer to this function so there is a single
\r
3246 place to maintain knowledge.
\r
3248 To restore the original behavior, just return FALSE unconditionally.
\r
3250 BOOL IsFullRepaintPreferrable()
\r
3252 BOOL result = FALSE;
\r
3254 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3255 /* Arrow may appear on the board */
\r
3263 This function is called by DrawPosition to know whether a full repaint must
\r
3266 Only DrawPosition may directly call this function, which makes use of
\r
3267 some state information. Other function should call DrawPosition specifying
\r
3268 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3270 BOOL DrawPositionNeedsFullRepaint()
\r
3272 BOOL result = FALSE;
\r
3275 Probably a slightly better policy would be to trigger a full repaint
\r
3276 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3277 but animation is fast enough that it's difficult to notice.
\r
3279 if( animInfo.piece == EmptySquare ) {
\r
3280 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3289 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3291 int row, column, x, y, square_color, piece_color;
\r
3292 ChessSquare piece;
\r
3294 HDC texture_hdc = NULL;
\r
3296 /* [AS] Initialize background textures if needed */
\r
3297 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3298 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3299 if( backTextureSquareSize != squareSize
\r
3300 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3301 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3302 backTextureSquareSize = squareSize;
\r
3303 RebuildTextureSquareInfo();
\r
3306 texture_hdc = CreateCompatibleDC( hdc );
\r
3309 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3310 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3312 SquareToPos(row, column, &x, &y);
\r
3314 piece = board[row][column];
\r
3316 square_color = ((column + row) % 2) == 1;
\r
3317 if( gameInfo.variant == VariantXiangqi ) {
\r
3318 square_color = !InPalace(row, column);
\r
3319 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3320 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3322 piece_color = (int) piece < (int) BlackPawn;
\r
3325 /* [HGM] holdings file: light square or black */
\r
3326 if(column == BOARD_LEFT-2) {
\r
3327 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3330 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3334 if(column == BOARD_RGHT + 1 ) {
\r
3335 if( row < gameInfo.holdingsSize )
\r
3338 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3342 if(column == BOARD_LEFT-1 ) /* left align */
\r
3343 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3344 else if( column == BOARD_RGHT) /* right align */
\r
3345 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3347 if (appData.monoMode) {
\r
3348 if (piece == EmptySquare) {
\r
3349 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3350 square_color ? WHITENESS : BLACKNESS);
\r
3352 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3355 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3356 /* [AS] Draw the square using a texture bitmap */
\r
3357 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3358 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3359 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3362 squareSize, squareSize,
\r
3365 backTextureSquareInfo[r][c].mode,
\r
3366 backTextureSquareInfo[r][c].x,
\r
3367 backTextureSquareInfo[r][c].y );
\r
3369 SelectObject( texture_hdc, hbm );
\r
3371 if (piece != EmptySquare) {
\r
3372 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3376 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3378 oldBrush = SelectObject(hdc, brush );
\r
3379 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3380 SelectObject(hdc, oldBrush);
\r
3381 if (piece != EmptySquare)
\r
3382 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3387 if( texture_hdc != NULL ) {
\r
3388 DeleteDC( texture_hdc );
\r
3392 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3393 void fputDW(FILE *f, int x)
\r
3395 fputc(x & 255, f);
\r
3396 fputc(x>>8 & 255, f);
\r
3397 fputc(x>>16 & 255, f);
\r
3398 fputc(x>>24 & 255, f);
\r
3401 #define MAX_CLIPS 200 /* more than enough */
\r
3404 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3406 // HBITMAP bufferBitmap;
\r
3411 int w = 100, h = 50;
\r
3413 if(logo == NULL) {
\r
3414 if(!logoHeight) return;
\r
3415 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3417 // GetClientRect(hwndMain, &Rect);
\r
3418 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3419 // Rect.bottom-Rect.top+1);
\r
3420 tmphdc = CreateCompatibleDC(hdc);
\r
3421 hbm = SelectObject(tmphdc, logo);
\r
3422 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3426 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3427 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3428 SelectObject(tmphdc, hbm);
\r
3436 HDC hdc = GetDC(hwndMain);
\r
3437 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3438 if(appData.autoLogo) {
\r
3440 switch(gameMode) { // pick logos based on game mode
\r
3441 case IcsObserving:
\r
3442 whiteLogo = second.programLogo; // ICS logo
\r
3443 blackLogo = second.programLogo;
\r
3446 case IcsPlayingWhite:
\r
3447 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3448 blackLogo = second.programLogo; // ICS logo
\r
3450 case IcsPlayingBlack:
\r
3451 whiteLogo = second.programLogo; // ICS logo
\r
3452 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3454 case TwoMachinesPlay:
\r
3455 if(first.twoMachinesColor[0] == 'b') {
\r
3456 whiteLogo = second.programLogo;
\r
3457 blackLogo = first.programLogo;
\r
3460 case MachinePlaysWhite:
\r
3461 blackLogo = userLogo;
\r
3463 case MachinePlaysBlack:
\r
3464 whiteLogo = userLogo;
\r
3465 blackLogo = first.programLogo;
\r
3468 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3469 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3470 ReleaseDC(hwndMain, hdc);
\r
3475 UpdateLogos(int display)
\r
3476 { // called after loading new engine(s), in tourney or from menu
\r
3477 LoadLogo(&first, 0, FALSE);
\r
3478 LoadLogo(&second, 1, appData.icsActive);
\r
3479 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3480 if(display) DisplayLogos();
\r
3483 static HDC hdcSeek;
\r
3485 // [HGM] seekgraph
\r
3486 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3489 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3490 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3491 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3492 SelectObject( hdcSeek, hp );
\r
3495 // front-end wrapper for drawing functions to do rectangles
\r
3496 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3501 if (hdcSeek == NULL) {
\r
3502 hdcSeek = GetDC(hwndMain);
\r
3503 if (!appData.monoMode) {
\r
3504 SelectPalette(hdcSeek, hPal, FALSE);
\r
3505 RealizePalette(hdcSeek);
\r
3508 hp = SelectObject( hdcSeek, gridPen );
\r
3509 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3510 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3511 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3512 SelectObject( hdcSeek, hp );
\r
3515 // front-end wrapper for putting text in graph
\r
3516 void DrawSeekText(char *buf, int x, int y)
\r
3519 SetBkMode( hdcSeek, TRANSPARENT );
\r
3520 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3521 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3524 void DrawSeekDot(int x, int y, int color)
\r
3526 int square = color & 0x80;
\r
3527 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3528 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3531 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3532 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3534 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3535 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3536 SelectObject(hdcSeek, oldBrush);
\r
3540 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3542 static Board lastReq[2], lastDrawn[2];
\r
3543 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3544 static int lastDrawnFlipView = 0;
\r
3545 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3546 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3549 HBITMAP bufferBitmap;
\r
3550 HBITMAP oldBitmap;
\r
3552 HRGN clips[MAX_CLIPS];
\r
3553 ChessSquare dragged_piece = EmptySquare;
\r
3554 int nr = twoBoards*partnerUp;
\r
3556 /* I'm undecided on this - this function figures out whether a full
\r
3557 * repaint is necessary on its own, so there's no real reason to have the
\r
3558 * caller tell it that. I think this can safely be set to FALSE - but
\r
3559 * if we trust the callers not to request full repaints unnessesarily, then
\r
3560 * we could skip some clipping work. In other words, only request a full
\r
3561 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3562 * gamestart and similar) --Hawk
\r
3564 Boolean fullrepaint = repaint;
\r
3566 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3568 if( DrawPositionNeedsFullRepaint() ) {
\r
3569 fullrepaint = TRUE;
\r
3572 if (board == NULL) {
\r
3573 if (!lastReqValid[nr]) {
\r
3576 board = lastReq[nr];
\r
3578 CopyBoard(lastReq[nr], board);
\r
3579 lastReqValid[nr] = 1;
\r
3582 if (doingSizing) {
\r
3586 if (IsIconic(hwndMain)) {
\r
3590 if (hdc == NULL) {
\r
3591 hdc = GetDC(hwndMain);
\r
3592 if (!appData.monoMode) {
\r
3593 SelectPalette(hdc, hPal, FALSE);
\r
3594 RealizePalette(hdc);
\r
3598 releaseDC = FALSE;
\r
3601 /* Create some work-DCs */
\r
3602 hdcmem = CreateCompatibleDC(hdc);
\r
3603 tmphdc = CreateCompatibleDC(hdc);
\r
3605 /* If dragging is in progress, we temporarely remove the piece */
\r
3606 /* [HGM] or temporarily decrease count if stacked */
\r
3607 /* !! Moved to before board compare !! */
\r
3608 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3609 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3610 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3611 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3612 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3614 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3615 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3616 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3618 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3621 /* Figure out which squares need updating by comparing the
\r
3622 * newest board with the last drawn board and checking if
\r
3623 * flipping has changed.
\r
3625 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3626 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3627 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3628 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3629 SquareToPos(row, column, &x, &y);
\r
3630 clips[num_clips++] =
\r
3631 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3635 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3636 for (i=0; i<2; i++) {
\r
3637 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3638 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3639 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3640 lastDrawnHighlight.sq[i].y >= 0) {
\r
3641 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3642 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3643 clips[num_clips++] =
\r
3644 CreateRectRgn(x - lineGap, y - lineGap,
\r
3645 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3647 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3648 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3649 clips[num_clips++] =
\r
3650 CreateRectRgn(x - lineGap, y - lineGap,
\r
3651 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3655 for (i=0; i<2; i++) {
\r
3656 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3657 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3658 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3659 lastDrawnPremove.sq[i].y >= 0) {
\r
3660 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3661 lastDrawnPremove.sq[i].x, &x, &y);
\r
3662 clips[num_clips++] =
\r
3663 CreateRectRgn(x - lineGap, y - lineGap,
\r
3664 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3666 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3667 premoveHighlightInfo.sq[i].y >= 0) {
\r
3668 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3669 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3670 clips[num_clips++] =
\r
3671 CreateRectRgn(x - lineGap, y - lineGap,
\r
3672 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3676 } else { // nr == 1
\r
3677 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3678 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3679 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3680 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3681 for (i=0; i<2; i++) {
\r
3682 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3683 partnerHighlightInfo.sq[i].y >= 0) {
\r
3684 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3685 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3686 clips[num_clips++] =
\r
3687 CreateRectRgn(x - lineGap, y - lineGap,
\r
3688 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3690 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3691 oldPartnerHighlight.sq[i].y >= 0) {
\r
3692 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3693 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3694 clips[num_clips++] =
\r
3695 CreateRectRgn(x - lineGap, y - lineGap,
\r
3696 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3701 fullrepaint = TRUE;
\r
3704 /* Create a buffer bitmap - this is the actual bitmap
\r
3705 * being written to. When all the work is done, we can
\r
3706 * copy it to the real DC (the screen). This avoids
\r
3707 * the problems with flickering.
\r
3709 GetClientRect(hwndMain, &Rect);
\r
3710 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3711 Rect.bottom-Rect.top+1);
\r
3712 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3713 if (!appData.monoMode) {
\r
3714 SelectPalette(hdcmem, hPal, FALSE);
\r
3717 /* Create clips for dragging */
\r
3718 if (!fullrepaint) {
\r
3719 if (dragInfo.from.x >= 0) {
\r
3720 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3721 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3723 if (dragInfo.start.x >= 0) {
\r
3724 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3725 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3727 if (dragInfo.pos.x >= 0) {
\r
3728 x = dragInfo.pos.x - squareSize / 2;
\r
3729 y = dragInfo.pos.y - squareSize / 2;
\r
3730 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3732 if (dragInfo.lastpos.x >= 0) {
\r
3733 x = dragInfo.lastpos.x - squareSize / 2;
\r
3734 y = dragInfo.lastpos.y - squareSize / 2;
\r
3735 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3739 /* Are we animating a move?
\r
3741 * - remove the piece from the board (temporarely)
\r
3742 * - calculate the clipping region
\r
3744 if (!fullrepaint) {
\r
3745 if (animInfo.piece != EmptySquare) {
\r
3746 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3747 x = boardRect.left + animInfo.lastpos.x;
\r
3748 y = boardRect.top + animInfo.lastpos.y;
\r
3749 x2 = boardRect.left + animInfo.pos.x;
\r
3750 y2 = boardRect.top + animInfo.pos.y;
\r
3751 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3752 /* Slight kludge. The real problem is that after AnimateMove is
\r
3753 done, the position on the screen does not match lastDrawn.
\r
3754 This currently causes trouble only on e.p. captures in
\r
3755 atomic, where the piece moves to an empty square and then
\r
3756 explodes. The old and new positions both had an empty square
\r
3757 at the destination, but animation has drawn a piece there and
\r
3758 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3759 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3763 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3764 if (num_clips == 0)
\r
3765 fullrepaint = TRUE;
\r
3767 /* Set clipping on the memory DC */
\r
3768 if (!fullrepaint) {
\r
3769 SelectClipRgn(hdcmem, clips[0]);
\r
3770 for (x = 1; x < num_clips; x++) {
\r
3771 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3772 abort(); // this should never ever happen!
\r
3776 /* Do all the drawing to the memory DC */
\r
3777 if(explodeInfo.radius) { // [HGM] atomic
\r
3779 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3780 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3781 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3782 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3783 x += squareSize/2;
\r
3784 y += squareSize/2;
\r
3785 if(!fullrepaint) {
\r
3786 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3787 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3789 DrawGridOnDC(hdcmem);
\r
3790 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3791 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3792 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3793 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3794 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3795 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3796 SelectObject(hdcmem, oldBrush);
\r
3798 DrawGridOnDC(hdcmem);
\r
3799 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3800 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3801 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3803 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3804 oldPartnerHighlight = partnerHighlightInfo;
\r
3806 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3808 if(nr == 0) // [HGM] dual: markers only on left board
\r
3809 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3810 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3811 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3812 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3813 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3814 SquareToPos(row, column, &x, &y);
\r
3815 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3816 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3817 SelectObject(hdcmem, oldBrush);
\r
3822 if( appData.highlightMoveWithArrow ) {
\r
3823 DrawArrowHighlight(hdcmem);
\r
3826 DrawCoordsOnDC(hdcmem);
\r
3828 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3829 /* to make sure lastDrawn contains what is actually drawn */
\r
3831 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3832 if (dragged_piece != EmptySquare) {
\r
3833 /* [HGM] or restack */
\r
3834 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3835 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3837 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3838 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3839 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3840 x = dragInfo.pos.x - squareSize / 2;
\r
3841 y = dragInfo.pos.y - squareSize / 2;
\r
3842 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3843 ((int) dragInfo.piece < (int) BlackPawn),
\r
3844 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3847 /* Put the animated piece back into place and draw it */
\r
3848 if (animInfo.piece != EmptySquare) {
\r
3849 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3850 x = boardRect.left + animInfo.pos.x;
\r
3851 y = boardRect.top + animInfo.pos.y;
\r
3852 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3853 ((int) animInfo.piece < (int) BlackPawn),
\r
3854 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3857 /* Release the bufferBitmap by selecting in the old bitmap
\r
3858 * and delete the memory DC
\r
3860 SelectObject(hdcmem, oldBitmap);
\r
3863 /* Set clipping on the target DC */
\r
3864 if (!fullrepaint) {
\r
3865 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3867 GetRgnBox(clips[x], &rect);
\r
3868 DeleteObject(clips[x]);
\r
3869 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3870 rect.right + wpMain.width/2, rect.bottom);
\r
3872 SelectClipRgn(hdc, clips[0]);
\r
3873 for (x = 1; x < num_clips; x++) {
\r
3874 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3875 abort(); // this should never ever happen!
\r
3879 /* Copy the new bitmap onto the screen in one go.
\r
3880 * This way we avoid any flickering
\r
3882 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3883 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3884 boardRect.right - boardRect.left,
\r
3885 boardRect.bottom - boardRect.top,
\r
3886 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3887 if(saveDiagFlag) {
\r
3888 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3889 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3891 GetObject(bufferBitmap, sizeof(b), &b);
\r
3892 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3893 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3894 bih.biWidth = b.bmWidth;
\r
3895 bih.biHeight = b.bmHeight;
\r
3897 bih.biBitCount = b.bmBitsPixel;
\r
3898 bih.biCompression = 0;
\r
3899 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3900 bih.biXPelsPerMeter = 0;
\r
3901 bih.biYPelsPerMeter = 0;
\r
3902 bih.biClrUsed = 0;
\r
3903 bih.biClrImportant = 0;
\r
3904 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3905 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3906 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3907 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3909 wb = b.bmWidthBytes;
\r
3911 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3912 int k = ((int*) pData)[i];
\r
3913 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3914 if(j >= 16) break;
\r
3916 if(j >= nrColors) nrColors = j+1;
\r
3918 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3920 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3921 for(w=0; w<(wb>>2); w+=2) {
\r
3922 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3923 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3924 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3925 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3926 pData[p++] = m | j<<4;
\r
3928 while(p&3) pData[p++] = 0;
\r
3931 wb = ((wb+31)>>5)<<2;
\r
3933 // write BITMAPFILEHEADER
\r
3934 fprintf(diagFile, "BM");
\r
3935 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3936 fputDW(diagFile, 0);
\r
3937 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3938 // write BITMAPINFOHEADER
\r
3939 fputDW(diagFile, 40);
\r
3940 fputDW(diagFile, b.bmWidth);
\r
3941 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3942 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3943 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3944 fputDW(diagFile, 0);
\r
3945 fputDW(diagFile, 0);
\r
3946 fputDW(diagFile, 0);
\r
3947 fputDW(diagFile, 0);
\r
3948 fputDW(diagFile, 0);
\r
3949 fputDW(diagFile, 0);
\r
3950 // write color table
\r
3952 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3953 // write bitmap data
\r
3954 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3955 fputc(pData[i], diagFile);
\r
3960 SelectObject(tmphdc, oldBitmap);
\r
3962 /* Massive cleanup */
\r
3963 for (x = 0; x < num_clips; x++)
\r
3964 DeleteObject(clips[x]);
\r
3967 DeleteObject(bufferBitmap);
\r
3970 ReleaseDC(hwndMain, hdc);
\r
3972 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3974 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3976 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3979 /* CopyBoard(lastDrawn, board);*/
\r
3980 lastDrawnHighlight = highlightInfo;
\r
3981 lastDrawnPremove = premoveHighlightInfo;
\r
3982 lastDrawnFlipView = flipView;
\r
3983 lastDrawnValid[nr] = 1;
\r
3986 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3991 saveDiagFlag = 1; diagFile = f;
\r
3992 HDCDrawPosition(NULL, TRUE, NULL);
\r
4000 /*---------------------------------------------------------------------------*\
\r
4001 | CLIENT PAINT PROCEDURE
\r
4002 | This is the main event-handler for the WM_PAINT message.
\r
4004 \*---------------------------------------------------------------------------*/
\r
4006 PaintProc(HWND hwnd)
\r
4012 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4013 if (IsIconic(hwnd)) {
\r
4014 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4016 if (!appData.monoMode) {
\r
4017 SelectPalette(hdc, hPal, FALSE);
\r
4018 RealizePalette(hdc);
\r
4020 HDCDrawPosition(hdc, 1, NULL);
\r
4021 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4022 flipView = !flipView; partnerUp = !partnerUp;
\r
4023 HDCDrawPosition(hdc, 1, NULL);
\r
4024 flipView = !flipView; partnerUp = !partnerUp;
\r
4027 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4028 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4029 ETO_CLIPPED|ETO_OPAQUE,
\r
4030 &messageRect, messageText, strlen(messageText), NULL);
\r
4031 SelectObject(hdc, oldFont);
\r
4032 DisplayBothClocks();
\r
4035 EndPaint(hwnd,&ps);
\r
4043 * If the user selects on a border boundary, return -1; if off the board,
\r
4044 * return -2. Otherwise map the event coordinate to the square.
\r
4045 * The offset boardRect.left or boardRect.top must already have been
\r
4046 * subtracted from x.
\r
4048 int EventToSquare(x, limit)
\r
4056 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4058 x /= (squareSize + lineGap);
\r
4070 DropEnable dropEnables[] = {
\r
4071 { 'P', DP_Pawn, N_("Pawn") },
\r
4072 { 'N', DP_Knight, N_("Knight") },
\r
4073 { 'B', DP_Bishop, N_("Bishop") },
\r
4074 { 'R', DP_Rook, N_("Rook") },
\r
4075 { 'Q', DP_Queen, N_("Queen") },
\r
4079 SetupDropMenu(HMENU hmenu)
\r
4081 int i, count, enable;
\r
4083 extern char white_holding[], black_holding[];
\r
4084 char item[MSG_SIZ];
\r
4086 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4087 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4088 dropEnables[i].piece);
\r
4090 while (p && *p++ == dropEnables[i].piece) count++;
\r
4091 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4092 enable = count > 0 || !appData.testLegality
\r
4093 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4094 && !appData.icsActive);
\r
4095 ModifyMenu(hmenu, dropEnables[i].command,
\r
4096 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4097 dropEnables[i].command, item);
\r
4101 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4103 dragInfo.lastpos.x = boardRect.left + x;
\r
4104 dragInfo.lastpos.y = boardRect.top + y;
\r
4105 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4106 dragInfo.from.x = fromX;
\r
4107 dragInfo.from.y = fromY;
\r
4108 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4109 dragInfo.start = dragInfo.from;
\r
4110 SetCapture(hwndMain);
\r
4113 void DragPieceEnd(int x, int y)
\r
4116 dragInfo.start.x = dragInfo.start.y = -1;
\r
4117 dragInfo.from = dragInfo.start;
\r
4118 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4121 void ChangeDragPiece(ChessSquare piece)
\r
4123 dragInfo.piece = piece;
\r
4126 /* Event handler for mouse messages */
\r
4128 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4132 static int recursive = 0;
\r
4134 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4137 if (message == WM_MBUTTONUP) {
\r
4138 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4139 to the middle button: we simulate pressing the left button too!
\r
4141 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4142 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4148 pt.x = LOWORD(lParam);
\r
4149 pt.y = HIWORD(lParam);
\r
4150 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4151 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4152 if (!flipView && y >= 0) {
\r
4153 y = BOARD_HEIGHT - 1 - y;
\r
4155 if (flipView && x >= 0) {
\r
4156 x = BOARD_WIDTH - 1 - x;
\r
4159 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4161 switch (message) {
\r
4162 case WM_LBUTTONDOWN:
\r
4163 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4164 ClockClick(flipClock); break;
\r
4165 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4166 ClockClick(!flipClock); break;
\r
4168 dragInfo.start.x = dragInfo.start.y = -1;
\r
4169 dragInfo.from = dragInfo.start;
\r
4170 if(fromX == -1 && frozen) { // not sure where this is for
\r
4171 fromX = fromY = -1;
\r
4172 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4175 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4176 DrawPosition(TRUE, NULL);
\r
4179 case WM_LBUTTONUP:
\r
4180 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4181 DrawPosition(TRUE, NULL);
\r
4184 case WM_MOUSEMOVE:
\r
4185 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4186 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4187 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4188 if ((appData.animateDragging || appData.highlightDragging)
\r
4189 && (wParam & MK_LBUTTON)
\r
4190 && dragInfo.from.x >= 0)
\r
4192 BOOL full_repaint = FALSE;
\r
4194 if (appData.animateDragging) {
\r
4195 dragInfo.pos = pt;
\r
4197 if (appData.highlightDragging) {
\r
4198 SetHighlights(fromX, fromY, x, y);
\r
4199 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4200 full_repaint = TRUE;
\r
4204 DrawPosition( full_repaint, NULL);
\r
4206 dragInfo.lastpos = dragInfo.pos;
\r
4210 case WM_MOUSEWHEEL: // [DM]
\r
4211 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4212 /* Mouse Wheel is being rolled forward
\r
4213 * Play moves forward
\r
4215 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4216 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4217 /* Mouse Wheel is being rolled backward
\r
4218 * Play moves backward
\r
4220 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4221 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4225 case WM_MBUTTONUP:
\r
4226 case WM_RBUTTONUP:
\r
4228 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4231 case WM_MBUTTONDOWN:
\r
4232 case WM_RBUTTONDOWN:
\r
4235 fromX = fromY = -1;
\r
4236 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4237 dragInfo.start.x = dragInfo.start.y = -1;
\r
4238 dragInfo.from = dragInfo.start;
\r
4239 dragInfo.lastpos = dragInfo.pos;
\r
4240 if (appData.highlightDragging) {
\r
4241 ClearHighlights();
\r
4244 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4245 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4246 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4247 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4248 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4252 DrawPosition(TRUE, NULL);
\r
4254 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4257 if (message == WM_MBUTTONDOWN) {
\r
4258 buttonCount = 3; /* even if system didn't think so */
\r
4259 if (wParam & MK_SHIFT)
\r
4260 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4262 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4263 } else { /* message == WM_RBUTTONDOWN */
\r
4264 /* Just have one menu, on the right button. Windows users don't
\r
4265 think to try the middle one, and sometimes other software steals
\r
4266 it, or it doesn't really exist. */
\r
4267 if(gameInfo.variant != VariantShogi)
\r
4268 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4270 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4274 SetCapture(hwndMain);
\r
4277 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4278 SetupDropMenu(hmenu);
\r
4279 MenuPopup(hwnd, pt, hmenu, -1);
\r
4289 /* Preprocess messages for buttons in main window */
\r
4291 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4293 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4296 for (i=0; i<N_BUTTONS; i++) {
\r
4297 if (buttonDesc[i].id == id) break;
\r
4299 if (i == N_BUTTONS) return 0;
\r
4300 switch (message) {
\r
4305 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4306 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4313 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4316 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4317 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4318 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4319 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4321 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4323 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4324 TypeInEvent((char)wParam);
\r
4330 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4333 /* Process messages for Promotion dialog box */
\r
4335 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4339 switch (message) {
\r
4340 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4341 /* Center the dialog over the application window */
\r
4342 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4343 Translate(hDlg, DLG_PromotionKing);
\r
4344 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4345 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4346 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4347 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4348 SW_SHOW : SW_HIDE);
\r
4349 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4350 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4351 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4352 PieceToChar(WhiteAngel) != '~') ||
\r
4353 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4354 PieceToChar(BlackAngel) != '~') ) ?
\r
4355 SW_SHOW : SW_HIDE);
\r
4356 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4357 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4358 PieceToChar(WhiteMarshall) != '~') ||
\r
4359 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4360 PieceToChar(BlackMarshall) != '~') ) ?
\r
4361 SW_SHOW : SW_HIDE);
\r
4362 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4363 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4364 gameInfo.variant != VariantShogi ?
\r
4365 SW_SHOW : SW_HIDE);
\r
4366 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4367 gameInfo.variant != VariantShogi ?
\r
4368 SW_SHOW : SW_HIDE);
\r
4369 if(gameInfo.variant == VariantShogi) {
\r
4370 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4371 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4372 SetWindowText(hDlg, "Promote?");
\r
4374 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4375 gameInfo.variant == VariantSuper ?
\r
4376 SW_SHOW : SW_HIDE);
\r
4379 case WM_COMMAND: /* message: received a command */
\r
4380 switch (LOWORD(wParam)) {
\r
4382 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4383 ClearHighlights();
\r
4384 DrawPosition(FALSE, NULL);
\r
4387 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4390 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4393 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4394 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4397 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4398 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4400 case PB_Chancellor:
\r
4401 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4403 case PB_Archbishop:
\r
4404 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4407 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4412 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4413 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4414 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4415 fromX = fromY = -1;
\r
4416 if (!appData.highlightLastMove) {
\r
4417 ClearHighlights();
\r
4418 DrawPosition(FALSE, NULL);
\r
4425 /* Pop up promotion dialog */
\r
4427 PromotionPopup(HWND hwnd)
\r
4431 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4432 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4433 hwnd, (DLGPROC)lpProc);
\r
4434 FreeProcInstance(lpProc);
\r
4440 DrawPosition(TRUE, NULL);
\r
4441 PromotionPopup(hwndMain);
\r
4444 /* Toggle ShowThinking */
\r
4446 ToggleShowThinking()
\r
4448 appData.showThinking = !appData.showThinking;
\r
4449 ShowThinkingEvent();
\r
4453 LoadGameDialog(HWND hwnd, char* title)
\r
4457 char fileTitle[MSG_SIZ];
\r
4458 f = OpenFileDialog(hwnd, "rb", "",
\r
4459 appData.oldSaveStyle ? "gam" : "pgn",
\r
4461 title, &number, fileTitle, NULL);
\r
4463 cmailMsgLoaded = FALSE;
\r
4464 if (number == 0) {
\r
4465 int error = GameListBuild(f);
\r
4467 DisplayError(_("Cannot build game list"), error);
\r
4468 } else if (!ListEmpty(&gameList) &&
\r
4469 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4470 GameListPopUp(f, fileTitle);
\r
4473 GameListDestroy();
\r
4476 LoadGame(f, number, fileTitle, FALSE);
\r
4480 int get_term_width()
\r
4485 HFONT hfont, hold_font;
\r
4490 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4494 // get the text metrics
\r
4495 hdc = GetDC(hText);
\r
4496 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4497 if (consoleCF.dwEffects & CFE_BOLD)
\r
4498 lf.lfWeight = FW_BOLD;
\r
4499 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4500 lf.lfItalic = TRUE;
\r
4501 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4502 lf.lfStrikeOut = TRUE;
\r
4503 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4504 lf.lfUnderline = TRUE;
\r
4505 hfont = CreateFontIndirect(&lf);
\r
4506 hold_font = SelectObject(hdc, hfont);
\r
4507 GetTextMetrics(hdc, &tm);
\r
4508 SelectObject(hdc, hold_font);
\r
4509 DeleteObject(hfont);
\r
4510 ReleaseDC(hText, hdc);
\r
4512 // get the rectangle
\r
4513 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4515 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4518 void UpdateICSWidth(HWND hText)
\r
4520 LONG old_width, new_width;
\r
4522 new_width = get_term_width(hText, FALSE);
\r
4523 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4524 if (new_width != old_width)
\r
4526 ics_update_width(new_width);
\r
4527 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4532 ChangedConsoleFont()
\r
4535 CHARRANGE tmpsel, sel;
\r
4536 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4537 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4538 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4541 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4542 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4543 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4544 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4545 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4546 * size. This was undocumented in the version of MSVC++ that I had
\r
4547 * when I wrote the code, but is apparently documented now.
\r
4549 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4550 cfmt.bCharSet = f->lf.lfCharSet;
\r
4551 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4552 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4553 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4554 /* Why are the following seemingly needed too? */
\r
4555 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4556 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4557 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4559 tmpsel.cpMax = -1; /*999999?*/
\r
4560 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4561 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4562 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4563 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4565 paraf.cbSize = sizeof(paraf);
\r
4566 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4567 paraf.dxStartIndent = 0;
\r
4568 paraf.dxOffset = WRAP_INDENT;
\r
4569 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4570 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4571 UpdateICSWidth(hText);
\r
4574 /*---------------------------------------------------------------------------*\
\r
4576 * Window Proc for main window
\r
4578 \*---------------------------------------------------------------------------*/
\r
4580 /* Process messages for main window, etc. */
\r
4582 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4585 int wmId, wmEvent;
\r
4589 char fileTitle[MSG_SIZ];
\r
4590 char buf[MSG_SIZ];
\r
4591 static SnapData sd;
\r
4592 static int peek=0;
\r
4594 switch (message) {
\r
4596 case WM_PAINT: /* message: repaint portion of window */
\r
4600 case WM_ERASEBKGND:
\r
4601 if (IsIconic(hwnd)) {
\r
4602 /* Cheat; change the message */
\r
4603 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4605 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4609 case WM_LBUTTONDOWN:
\r
4610 case WM_MBUTTONDOWN:
\r
4611 case WM_RBUTTONDOWN:
\r
4612 case WM_LBUTTONUP:
\r
4613 case WM_MBUTTONUP:
\r
4614 case WM_RBUTTONUP:
\r
4615 case WM_MOUSEMOVE:
\r
4616 case WM_MOUSEWHEEL:
\r
4617 MouseEvent(hwnd, message, wParam, lParam);
\r
4621 if((char)wParam == '\b') {
\r
4622 ForwardEvent(); peek = 0;
\r
4625 JAWS_KBUP_NAVIGATION
\r
4630 if((char)wParam == '\b') {
\r
4631 if(!peek) BackwardEvent(), peek = 1;
\r
4634 JAWS_KBDOWN_NAVIGATION
\r
4640 JAWS_ALT_INTERCEPT
\r
4642 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4643 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4644 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4645 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4647 SendMessage(h, message, wParam, lParam);
\r
4648 } else if(lParam != KF_REPEAT) {
\r
4649 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4650 TypeInEvent((char)wParam);
\r
4651 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4652 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4657 case WM_PALETTECHANGED:
\r
4658 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4660 HDC hdc = GetDC(hwndMain);
\r
4661 SelectPalette(hdc, hPal, TRUE);
\r
4662 nnew = RealizePalette(hdc);
\r
4664 paletteChanged = TRUE;
\r
4665 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4667 ReleaseDC(hwnd, hdc);
\r
4671 case WM_QUERYNEWPALETTE:
\r
4672 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4674 HDC hdc = GetDC(hwndMain);
\r
4675 paletteChanged = FALSE;
\r
4676 SelectPalette(hdc, hPal, FALSE);
\r
4677 nnew = RealizePalette(hdc);
\r
4679 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4681 ReleaseDC(hwnd, hdc);
\r
4686 case WM_COMMAND: /* message: command from application menu */
\r
4687 wmId = LOWORD(wParam);
\r
4688 wmEvent = HIWORD(wParam);
\r
4693 SAY("new game enter a move to play against the computer with white");
\r
4696 case IDM_NewGameFRC:
\r
4697 if( NewGameFRC() == 0 ) {
\r
4702 case IDM_NewVariant:
\r
4703 NewVariantPopup(hwnd);
\r
4706 case IDM_LoadGame:
\r
4707 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4710 case IDM_LoadNextGame:
\r
4714 case IDM_LoadPrevGame:
\r
4718 case IDM_ReloadGame:
\r
4722 case IDM_LoadPosition:
\r
4723 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4724 Reset(FALSE, TRUE);
\r
4727 f = OpenFileDialog(hwnd, "rb", "",
\r
4728 appData.oldSaveStyle ? "pos" : "fen",
\r
4730 _("Load Position from File"), &number, fileTitle, NULL);
\r
4732 LoadPosition(f, number, fileTitle);
\r
4736 case IDM_LoadNextPosition:
\r
4737 ReloadPosition(1);
\r
4740 case IDM_LoadPrevPosition:
\r
4741 ReloadPosition(-1);
\r
4744 case IDM_ReloadPosition:
\r
4745 ReloadPosition(0);
\r
4748 case IDM_SaveGame:
\r
4749 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4750 f = OpenFileDialog(hwnd, "a", defName,
\r
4751 appData.oldSaveStyle ? "gam" : "pgn",
\r
4753 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4755 SaveGame(f, 0, "");
\r
4759 case IDM_SavePosition:
\r
4760 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4761 f = OpenFileDialog(hwnd, "a", defName,
\r
4762 appData.oldSaveStyle ? "pos" : "fen",
\r
4764 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4766 SavePosition(f, 0, "");
\r
4770 case IDM_SaveDiagram:
\r
4771 defName = "diagram";
\r
4772 f = OpenFileDialog(hwnd, "wb", defName,
\r
4775 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4781 case IDM_CopyGame:
\r
4782 CopyGameToClipboard();
\r
4785 case IDM_PasteGame:
\r
4786 PasteGameFromClipboard();
\r
4789 case IDM_CopyGameListToClipboard:
\r
4790 CopyGameListToClipboard();
\r
4793 /* [AS] Autodetect FEN or PGN data */
\r
4794 case IDM_PasteAny:
\r
4795 PasteGameOrFENFromClipboard();
\r
4798 /* [AS] Move history */
\r
4799 case IDM_ShowMoveHistory:
\r
4800 if( MoveHistoryIsUp() ) {
\r
4801 MoveHistoryPopDown();
\r
4804 MoveHistoryPopUp();
\r
4808 /* [AS] Eval graph */
\r
4809 case IDM_ShowEvalGraph:
\r
4810 if( EvalGraphIsUp() ) {
\r
4811 EvalGraphPopDown();
\r
4815 SetFocus(hwndMain);
\r
4819 /* [AS] Engine output */
\r
4820 case IDM_ShowEngineOutput:
\r
4821 if( EngineOutputIsUp() ) {
\r
4822 EngineOutputPopDown();
\r
4825 EngineOutputPopUp();
\r
4829 /* [AS] User adjudication */
\r
4830 case IDM_UserAdjudication_White:
\r
4831 UserAdjudicationEvent( +1 );
\r
4834 case IDM_UserAdjudication_Black:
\r
4835 UserAdjudicationEvent( -1 );
\r
4838 case IDM_UserAdjudication_Draw:
\r
4839 UserAdjudicationEvent( 0 );
\r
4842 /* [AS] Game list options dialog */
\r
4843 case IDM_GameListOptions:
\r
4844 GameListOptions();
\r
4851 case IDM_CopyPosition:
\r
4852 CopyFENToClipboard();
\r
4855 case IDM_PastePosition:
\r
4856 PasteFENFromClipboard();
\r
4859 case IDM_MailMove:
\r
4863 case IDM_ReloadCMailMsg:
\r
4864 Reset(TRUE, TRUE);
\r
4865 ReloadCmailMsgEvent(FALSE);
\r
4868 case IDM_Minimize:
\r
4869 ShowWindow(hwnd, SW_MINIMIZE);
\r
4876 case IDM_MachineWhite:
\r
4877 MachineWhiteEvent();
\r
4879 * refresh the tags dialog only if it's visible
\r
4881 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4883 tags = PGNTags(&gameInfo);
\r
4884 TagsPopUp(tags, CmailMsg());
\r
4887 SAY("computer starts playing white");
\r
4890 case IDM_MachineBlack:
\r
4891 MachineBlackEvent();
\r
4893 * refresh the tags dialog only if it's visible
\r
4895 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4897 tags = PGNTags(&gameInfo);
\r
4898 TagsPopUp(tags, CmailMsg());
\r
4901 SAY("computer starts playing black");
\r
4904 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4905 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4908 case IDM_TwoMachines:
\r
4909 TwoMachinesEvent();
\r
4911 * refresh the tags dialog only if it's visible
\r
4913 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4915 tags = PGNTags(&gameInfo);
\r
4916 TagsPopUp(tags, CmailMsg());
\r
4919 SAY("computer starts playing both sides");
\r
4922 case IDM_AnalysisMode:
\r
4923 if (!first.analysisSupport) {
\r
4924 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4925 DisplayError(buf, 0);
\r
4927 SAY("analyzing current position");
\r
4928 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4929 if (appData.icsActive) {
\r
4930 if (gameMode != IcsObserving) {
\r
4931 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4932 DisplayError(buf, 0);
\r
4933 /* secure check */
\r
4934 if (appData.icsEngineAnalyze) {
\r
4935 if (appData.debugMode)
\r
4936 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4937 ExitAnalyzeMode();
\r
4943 /* if enable, user want disable icsEngineAnalyze */
\r
4944 if (appData.icsEngineAnalyze) {
\r
4945 ExitAnalyzeMode();
\r
4949 appData.icsEngineAnalyze = TRUE;
\r
4950 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4953 if (!appData.showThinking) ToggleShowThinking();
\r
4954 AnalyzeModeEvent();
\r
4958 case IDM_AnalyzeFile:
\r
4959 if (!first.analysisSupport) {
\r
4960 char buf[MSG_SIZ];
\r
4961 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4962 DisplayError(buf, 0);
\r
4964 if (!appData.showThinking) ToggleShowThinking();
\r
4965 AnalyzeFileEvent();
\r
4966 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4967 AnalysisPeriodicEvent(1);
\r
4971 case IDM_IcsClient:
\r
4975 case IDM_EditGame:
\r
4976 case IDM_EditGame2:
\r
4981 case IDM_EditPosition:
\r
4982 case IDM_EditPosition2:
\r
4983 EditPositionEvent();
\r
4984 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4987 case IDM_Training:
\r
4991 case IDM_ShowGameList:
\r
4992 ShowGameListProc();
\r
4995 case IDM_EditProgs1:
\r
4996 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4999 case IDM_EditProgs2:
\r
5000 LoadEnginePopUp(hwndMain);
\r
5001 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
5004 case IDM_EditServers:
\r
5005 EditTagsPopUp(icsNames, &icsNames);
\r
5008 case IDM_EditTags:
\r
5013 case IDM_EditBook:
\r
5017 case IDM_EditComment:
\r
5019 if (commentUp && editComment) {
\r
5022 EditCommentEvent();
\r
5042 case IDM_CallFlag:
\r
5062 case IDM_StopObserving:
\r
5063 StopObservingEvent();
\r
5066 case IDM_StopExamining:
\r
5067 StopExaminingEvent();
\r
5071 UploadGameEvent();
\r
5074 case IDM_TypeInMove:
\r
5075 TypeInEvent('\000');
\r
5078 case IDM_TypeInName:
\r
5079 PopUpNameDialog('\000');
\r
5082 case IDM_Backward:
\r
5084 SetFocus(hwndMain);
\r
5091 SetFocus(hwndMain);
\r
5096 SetFocus(hwndMain);
\r
5101 SetFocus(hwndMain);
\r
5104 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5105 case OPT_GameListPrev:
\r
5106 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5110 RevertEvent(FALSE);
\r
5113 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5114 RevertEvent(TRUE);
\r
5117 case IDM_TruncateGame:
\r
5118 TruncateGameEvent();
\r
5125 case IDM_RetractMove:
\r
5126 RetractMoveEvent();
\r
5129 case IDM_FlipView:
\r
5130 flipView = !flipView;
\r
5131 DrawPosition(FALSE, NULL);
\r
5134 case IDM_FlipClock:
\r
5135 flipClock = !flipClock;
\r
5136 DisplayBothClocks();
\r
5140 case IDM_MuteSounds:
\r
5141 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5142 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5143 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5146 case IDM_GeneralOptions:
\r
5147 GeneralOptionsPopup(hwnd);
\r
5148 DrawPosition(TRUE, NULL);
\r
5151 case IDM_BoardOptions:
\r
5152 BoardOptionsPopup(hwnd);
\r
5155 case IDM_EnginePlayOptions:
\r
5156 EnginePlayOptionsPopup(hwnd);
\r
5159 case IDM_Engine1Options:
\r
5160 EngineOptionsPopup(hwnd, &first);
\r
5163 case IDM_Engine2Options:
\r
5165 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5166 EngineOptionsPopup(hwnd, &second);
\r
5169 case IDM_OptionsUCI:
\r
5170 UciOptionsPopup(hwnd);
\r
5174 TourneyPopup(hwnd);
\r
5177 case IDM_IcsOptions:
\r
5178 IcsOptionsPopup(hwnd);
\r
5182 FontsOptionsPopup(hwnd);
\r
5186 SoundOptionsPopup(hwnd);
\r
5189 case IDM_CommPort:
\r
5190 CommPortOptionsPopup(hwnd);
\r
5193 case IDM_LoadOptions:
\r
5194 LoadOptionsPopup(hwnd);
\r
5197 case IDM_SaveOptions:
\r
5198 SaveOptionsPopup(hwnd);
\r
5201 case IDM_TimeControl:
\r
5202 TimeControlOptionsPopup(hwnd);
\r
5205 case IDM_SaveSettings:
\r
5206 SaveSettings(settingsFileName);
\r
5209 case IDM_SaveSettingsOnExit:
\r
5210 saveSettingsOnExit = !saveSettingsOnExit;
\r
5211 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5212 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5213 MF_CHECKED : MF_UNCHECKED));
\r
5224 case IDM_AboutGame:
\r
5229 appData.debugMode = !appData.debugMode;
\r
5230 if (appData.debugMode) {
\r
5231 char dir[MSG_SIZ];
\r
5232 GetCurrentDirectory(MSG_SIZ, dir);
\r
5233 SetCurrentDirectory(installDir);
\r
5234 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5235 SetCurrentDirectory(dir);
\r
5236 setbuf(debugFP, NULL);
\r
5243 case IDM_HELPCONTENTS:
\r
5244 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5245 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5246 MessageBox (GetFocus(),
\r
5247 _("Unable to activate help"),
\r
5248 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5252 case IDM_HELPSEARCH:
\r
5253 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5254 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5255 MessageBox (GetFocus(),
\r
5256 _("Unable to activate help"),
\r
5257 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5261 case IDM_HELPHELP:
\r
5262 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5263 MessageBox (GetFocus(),
\r
5264 _("Unable to activate help"),
\r
5265 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5270 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5272 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5273 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5274 FreeProcInstance(lpProc);
\r
5277 case IDM_DirectCommand1:
\r
5278 AskQuestionEvent(_("Direct Command"),
\r
5279 _("Send to chess program:"), "", "1");
\r
5281 case IDM_DirectCommand2:
\r
5282 AskQuestionEvent(_("Direct Command"),
\r
5283 _("Send to second chess program:"), "", "2");
\r
5286 case EP_WhitePawn:
\r
5287 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5288 fromX = fromY = -1;
\r
5291 case EP_WhiteKnight:
\r
5292 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5293 fromX = fromY = -1;
\r
5296 case EP_WhiteBishop:
\r
5297 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5298 fromX = fromY = -1;
\r
5301 case EP_WhiteRook:
\r
5302 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5303 fromX = fromY = -1;
\r
5306 case EP_WhiteQueen:
\r
5307 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5308 fromX = fromY = -1;
\r
5311 case EP_WhiteFerz:
\r
5312 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5313 fromX = fromY = -1;
\r
5316 case EP_WhiteWazir:
\r
5317 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5318 fromX = fromY = -1;
\r
5321 case EP_WhiteAlfil:
\r
5322 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5323 fromX = fromY = -1;
\r
5326 case EP_WhiteCannon:
\r
5327 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5328 fromX = fromY = -1;
\r
5331 case EP_WhiteCardinal:
\r
5332 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5333 fromX = fromY = -1;
\r
5336 case EP_WhiteMarshall:
\r
5337 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5338 fromX = fromY = -1;
\r
5341 case EP_WhiteKing:
\r
5342 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5343 fromX = fromY = -1;
\r
5346 case EP_BlackPawn:
\r
5347 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5348 fromX = fromY = -1;
\r
5351 case EP_BlackKnight:
\r
5352 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5353 fromX = fromY = -1;
\r
5356 case EP_BlackBishop:
\r
5357 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5358 fromX = fromY = -1;
\r
5361 case EP_BlackRook:
\r
5362 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5363 fromX = fromY = -1;
\r
5366 case EP_BlackQueen:
\r
5367 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5368 fromX = fromY = -1;
\r
5371 case EP_BlackFerz:
\r
5372 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5373 fromX = fromY = -1;
\r
5376 case EP_BlackWazir:
\r
5377 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5378 fromX = fromY = -1;
\r
5381 case EP_BlackAlfil:
\r
5382 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5383 fromX = fromY = -1;
\r
5386 case EP_BlackCannon:
\r
5387 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5388 fromX = fromY = -1;
\r
5391 case EP_BlackCardinal:
\r
5392 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5393 fromX = fromY = -1;
\r
5396 case EP_BlackMarshall:
\r
5397 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5398 fromX = fromY = -1;
\r
5401 case EP_BlackKing:
\r
5402 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5403 fromX = fromY = -1;
\r
5406 case EP_EmptySquare:
\r
5407 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5408 fromX = fromY = -1;
\r
5411 case EP_ClearBoard:
\r
5412 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5413 fromX = fromY = -1;
\r
5417 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5418 fromX = fromY = -1;
\r
5422 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5423 fromX = fromY = -1;
\r
5427 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5428 fromX = fromY = -1;
\r
5432 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5433 fromX = fromY = -1;
\r
5437 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5438 fromX = fromY = -1;
\r
5442 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5443 fromX = fromY = -1;
\r
5447 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5448 fromX = fromY = -1;
\r
5452 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5453 fromX = fromY = -1;
\r
5457 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5458 fromX = fromY = -1;
\r
5462 barbaric = 0; appData.language = "";
\r
5463 TranslateMenus(0);
\r
5464 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5465 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5466 lastChecked = wmId;
\r
5470 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5471 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5473 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5474 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5475 TranslateMenus(0);
\r
5476 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5477 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5478 lastChecked = wmId;
\r
5481 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5487 case CLOCK_TIMER_ID:
\r
5488 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5489 clockTimerEvent = 0;
\r
5490 DecrementClocks(); /* call into back end */
\r
5492 case LOAD_GAME_TIMER_ID:
\r
5493 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5494 loadGameTimerEvent = 0;
\r
5495 AutoPlayGameLoop(); /* call into back end */
\r
5497 case ANALYSIS_TIMER_ID:
\r
5498 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5499 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5500 AnalysisPeriodicEvent(0);
\r
5502 KillTimer(hwnd, analysisTimerEvent);
\r
5503 analysisTimerEvent = 0;
\r
5506 case DELAYED_TIMER_ID:
\r
5507 KillTimer(hwnd, delayedTimerEvent);
\r
5508 delayedTimerEvent = 0;
\r
5509 delayedTimerCallback();
\r
5514 case WM_USER_Input:
\r
5515 InputEvent(hwnd, message, wParam, lParam);
\r
5518 /* [AS] Also move "attached" child windows */
\r
5519 case WM_WINDOWPOSCHANGING:
\r
5521 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5522 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5524 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5525 /* Window is moving */
\r
5528 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5529 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5530 rcMain.right = wpMain.x + wpMain.width;
\r
5531 rcMain.top = wpMain.y;
\r
5532 rcMain.bottom = wpMain.y + wpMain.height;
\r
5534 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5535 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5536 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5537 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5538 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5539 wpMain.x = lpwp->x;
\r
5540 wpMain.y = lpwp->y;
\r
5545 /* [AS] Snapping */
\r
5546 case WM_ENTERSIZEMOVE:
\r
5547 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5548 if (hwnd == hwndMain) {
\r
5549 doingSizing = TRUE;
\r
5552 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5556 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5557 if (hwnd == hwndMain) {
\r
5558 lastSizing = wParam;
\r
5563 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5564 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5566 case WM_EXITSIZEMOVE:
\r
5567 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5568 if (hwnd == hwndMain) {
\r
5570 doingSizing = FALSE;
\r
5571 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5572 GetClientRect(hwnd, &client);
\r
5573 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5575 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5577 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5580 case WM_DESTROY: /* message: window being destroyed */
\r
5581 PostQuitMessage(0);
\r
5585 if (hwnd == hwndMain) {
\r
5590 default: /* Passes it on if unprocessed */
\r
5591 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5596 /*---------------------------------------------------------------------------*\
\r
5598 * Misc utility routines
\r
5600 \*---------------------------------------------------------------------------*/
\r
5603 * Decent random number generator, at least not as bad as Windows
\r
5604 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5606 unsigned int randstate;
\r
5611 randstate = randstate * 1664525 + 1013904223;
\r
5612 return (int) randstate & 0x7fffffff;
\r
5616 mysrandom(unsigned int seed)
\r
5623 * returns TRUE if user selects a different color, FALSE otherwise
\r
5627 ChangeColor(HWND hwnd, COLORREF *which)
\r
5629 static BOOL firstTime = TRUE;
\r
5630 static DWORD customColors[16];
\r
5632 COLORREF newcolor;
\r
5637 /* Make initial colors in use available as custom colors */
\r
5638 /* Should we put the compiled-in defaults here instead? */
\r
5640 customColors[i++] = lightSquareColor & 0xffffff;
\r
5641 customColors[i++] = darkSquareColor & 0xffffff;
\r
5642 customColors[i++] = whitePieceColor & 0xffffff;
\r
5643 customColors[i++] = blackPieceColor & 0xffffff;
\r
5644 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5645 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5647 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5648 customColors[i++] = textAttribs[ccl].color;
\r
5650 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5651 firstTime = FALSE;
\r
5654 cc.lStructSize = sizeof(cc);
\r
5655 cc.hwndOwner = hwnd;
\r
5656 cc.hInstance = NULL;
\r
5657 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5658 cc.lpCustColors = (LPDWORD) customColors;
\r
5659 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5661 if (!ChooseColor(&cc)) return FALSE;
\r
5663 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5664 if (newcolor == *which) return FALSE;
\r
5665 *which = newcolor;
\r
5669 InitDrawingColors();
\r
5670 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5675 MyLoadSound(MySound *ms)
\r
5681 if (ms->data && ms->flag) free(ms->data);
\r
5684 switch (ms->name[0]) {
\r
5690 /* System sound from Control Panel. Don't preload here. */
\r
5694 if (ms->name[1] == NULLCHAR) {
\r
5695 /* "!" alone = silence */
\r
5698 /* Builtin wave resource. Error if not found. */
\r
5699 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5700 if (h == NULL) break;
\r
5701 ms->data = (void *)LoadResource(hInst, h);
\r
5702 ms->flag = 0; // not maloced, so cannot be freed!
\r
5703 if (h == NULL) break;
\r
5708 /* .wav file. Error if not found. */
\r
5709 f = fopen(ms->name, "rb");
\r
5710 if (f == NULL) break;
\r
5711 if (fstat(fileno(f), &st) < 0) break;
\r
5712 ms->data = malloc(st.st_size);
\r
5714 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5720 char buf[MSG_SIZ];
\r
5721 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5722 DisplayError(buf, GetLastError());
\r
5728 MyPlaySound(MySound *ms)
\r
5730 BOOLEAN ok = FALSE;
\r
5732 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5733 switch (ms->name[0]) {
\r
5735 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5740 /* System sound from Control Panel (deprecated feature).
\r
5741 "$" alone or an unset sound name gets default beep (still in use). */
\r
5742 if (ms->name[1]) {
\r
5743 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5745 if (!ok) ok = MessageBeep(MB_OK);
\r
5748 /* Builtin wave resource, or "!" alone for silence */
\r
5749 if (ms->name[1]) {
\r
5750 if (ms->data == NULL) return FALSE;
\r
5751 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5757 /* .wav file. Error if not found. */
\r
5758 if (ms->data == NULL) return FALSE;
\r
5759 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5762 /* Don't print an error: this can happen innocently if the sound driver
\r
5763 is busy; for instance, if another instance of WinBoard is playing
\r
5764 a sound at about the same time. */
\r
5770 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5773 OPENFILENAME *ofn;
\r
5774 static UINT *number; /* gross that this is static */
\r
5776 switch (message) {
\r
5777 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5778 /* Center the dialog over the application window */
\r
5779 ofn = (OPENFILENAME *) lParam;
\r
5780 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5781 number = (UINT *) ofn->lCustData;
\r
5782 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5786 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5787 Translate(hDlg, 1536);
\r
5788 return FALSE; /* Allow for further processing */
\r
5791 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5792 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5794 return FALSE; /* Allow for further processing */
\r
5800 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5802 static UINT *number;
\r
5803 OPENFILENAME *ofname;
\r
5806 case WM_INITDIALOG:
\r
5807 Translate(hdlg, DLG_IndexNumber);
\r
5808 ofname = (OPENFILENAME *)lParam;
\r
5809 number = (UINT *)(ofname->lCustData);
\r
5812 ofnot = (OFNOTIFY *)lParam;
\r
5813 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5814 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5823 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5824 char *nameFilt, char *dlgTitle, UINT *number,
\r
5825 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5827 OPENFILENAME openFileName;
\r
5828 char buf1[MSG_SIZ];
\r
5831 if (fileName == NULL) fileName = buf1;
\r
5832 if (defName == NULL) {
\r
5833 safeStrCpy(fileName, "*.", 3 );
\r
5834 strcat(fileName, defExt);
\r
5836 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5838 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5839 if (number) *number = 0;
\r
5841 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5842 openFileName.hwndOwner = hwnd;
\r
5843 openFileName.hInstance = (HANDLE) hInst;
\r
5844 openFileName.lpstrFilter = nameFilt;
\r
5845 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5846 openFileName.nMaxCustFilter = 0L;
\r
5847 openFileName.nFilterIndex = 1L;
\r
5848 openFileName.lpstrFile = fileName;
\r
5849 openFileName.nMaxFile = MSG_SIZ;
\r
5850 openFileName.lpstrFileTitle = fileTitle;
\r
5851 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5852 openFileName.lpstrInitialDir = NULL;
\r
5853 openFileName.lpstrTitle = dlgTitle;
\r
5854 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5855 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5856 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5857 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5858 openFileName.nFileOffset = 0;
\r
5859 openFileName.nFileExtension = 0;
\r
5860 openFileName.lpstrDefExt = defExt;
\r
5861 openFileName.lCustData = (LONG) number;
\r
5862 openFileName.lpfnHook = oldDialog ?
\r
5863 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5864 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5866 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5867 GetOpenFileName(&openFileName)) {
\r
5868 /* open the file */
\r
5869 f = fopen(openFileName.lpstrFile, write);
\r
5871 MessageBox(hwnd, _("File open failed"), NULL,
\r
5872 MB_OK|MB_ICONEXCLAMATION);
\r
5876 int err = CommDlgExtendedError();
\r
5877 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5886 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5888 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5891 * Get the first pop-up menu in the menu template. This is the
\r
5892 * menu that TrackPopupMenu displays.
\r
5894 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5895 TranslateOneMenu(10, hmenuTrackPopup);
\r
5897 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5900 * TrackPopup uses screen coordinates, so convert the
\r
5901 * coordinates of the mouse click to screen coordinates.
\r
5903 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5905 /* Draw and track the floating pop-up menu. */
\r
5906 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5907 pt.x, pt.y, 0, hwnd, NULL);
\r
5909 /* Destroy the menu.*/
\r
5910 DestroyMenu(hmenu);
\r
5915 int sizeX, sizeY, newSizeX, newSizeY;
\r
5917 } ResizeEditPlusButtonsClosure;
\r
5920 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5922 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5926 if (hChild == cl->hText) return TRUE;
\r
5927 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5928 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5929 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5930 ScreenToClient(cl->hDlg, &pt);
\r
5931 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5932 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5936 /* Resize a dialog that has a (rich) edit field filling most of
\r
5937 the top, with a row of buttons below */
\r
5939 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5942 int newTextHeight, newTextWidth;
\r
5943 ResizeEditPlusButtonsClosure cl;
\r
5945 /*if (IsIconic(hDlg)) return;*/
\r
5946 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5948 cl.hdwp = BeginDeferWindowPos(8);
\r
5950 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5951 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5952 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5953 if (newTextHeight < 0) {
\r
5954 newSizeY += -newTextHeight;
\r
5955 newTextHeight = 0;
\r
5957 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5958 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5964 cl.newSizeX = newSizeX;
\r
5965 cl.newSizeY = newSizeY;
\r
5966 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5968 EndDeferWindowPos(cl.hdwp);
\r
5971 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5973 RECT rChild, rParent;
\r
5974 int wChild, hChild, wParent, hParent;
\r
5975 int wScreen, hScreen, xNew, yNew;
\r
5978 /* Get the Height and Width of the child window */
\r
5979 GetWindowRect (hwndChild, &rChild);
\r
5980 wChild = rChild.right - rChild.left;
\r
5981 hChild = rChild.bottom - rChild.top;
\r
5983 /* Get the Height and Width of the parent window */
\r
5984 GetWindowRect (hwndParent, &rParent);
\r
5985 wParent = rParent.right - rParent.left;
\r
5986 hParent = rParent.bottom - rParent.top;
\r
5988 /* Get the display limits */
\r
5989 hdc = GetDC (hwndChild);
\r
5990 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5991 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5992 ReleaseDC(hwndChild, hdc);
\r
5994 /* Calculate new X position, then adjust for screen */
\r
5995 xNew = rParent.left + ((wParent - wChild) /2);
\r
5998 } else if ((xNew+wChild) > wScreen) {
\r
5999 xNew = wScreen - wChild;
\r
6002 /* Calculate new Y position, then adjust for screen */
\r
6004 yNew = rParent.top + ((hParent - hChild) /2);
\r
6007 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6012 } else if ((yNew+hChild) > hScreen) {
\r
6013 yNew = hScreen - hChild;
\r
6016 /* Set it, and return */
\r
6017 return SetWindowPos (hwndChild, NULL,
\r
6018 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6021 /* Center one window over another */
\r
6022 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6024 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6027 /*---------------------------------------------------------------------------*\
\r
6029 * Startup Dialog functions
\r
6031 \*---------------------------------------------------------------------------*/
\r
6033 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6035 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6037 while (*cd != NULL) {
\r
6038 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6044 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6046 char buf1[MAX_ARG_LEN];
\r
6049 if (str[0] == '@') {
\r
6050 FILE* f = fopen(str + 1, "r");
\r
6052 DisplayFatalError(str + 1, errno, 2);
\r
6055 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6057 buf1[len] = NULLCHAR;
\r
6061 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6064 char buf[MSG_SIZ];
\r
6065 char *end = strchr(str, '\n');
\r
6066 if (end == NULL) return;
\r
6067 memcpy(buf, str, end - str);
\r
6068 buf[end - str] = NULLCHAR;
\r
6069 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6075 SetStartupDialogEnables(HWND hDlg)
\r
6077 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6078 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6079 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6080 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6081 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6082 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6083 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6084 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6085 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6086 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6087 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6088 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6089 IsDlgButtonChecked(hDlg, OPT_View));
\r
6093 QuoteForFilename(char *filename)
\r
6095 int dquote, space;
\r
6096 dquote = strchr(filename, '"') != NULL;
\r
6097 space = strchr(filename, ' ') != NULL;
\r
6098 if (dquote || space) {
\r
6110 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6112 char buf[MSG_SIZ];
\r
6115 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6116 q = QuoteForFilename(nthcp);
\r
6117 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6118 if (*nthdir != NULLCHAR) {
\r
6119 q = QuoteForFilename(nthdir);
\r
6120 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6122 if (*nthcp == NULLCHAR) {
\r
6123 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6124 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6125 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6126 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6131 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6133 char buf[MSG_SIZ];
\r
6137 switch (message) {
\r
6138 case WM_INITDIALOG:
\r
6139 /* Center the dialog */
\r
6140 CenterWindow (hDlg, GetDesktopWindow());
\r
6141 Translate(hDlg, DLG_Startup);
\r
6142 /* Initialize the dialog items */
\r
6143 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6144 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6145 firstChessProgramNames);
\r
6146 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6147 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6148 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6149 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6150 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6151 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6152 if (*appData.icsHelper != NULLCHAR) {
\r
6153 char *q = QuoteForFilename(appData.icsHelper);
\r
6154 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6156 if (*appData.icsHost == NULLCHAR) {
\r
6157 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6158 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6159 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6160 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6161 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6164 if (appData.icsActive) {
\r
6165 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6167 else if (appData.noChessProgram) {
\r
6168 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6171 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6174 SetStartupDialogEnables(hDlg);
\r
6178 switch (LOWORD(wParam)) {
\r
6180 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\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 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6185 ParseArgs(StringGet, &p);
\r
6186 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6187 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6189 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6190 ParseArgs(StringGet, &p);
\r
6191 SwapEngines(singleList); // ... and then make it 'second'
\r
6192 appData.noChessProgram = FALSE;
\r
6193 appData.icsActive = FALSE;
\r
6194 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6195 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6196 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6198 ParseArgs(StringGet, &p);
\r
6199 if (appData.zippyPlay) {
\r
6200 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6201 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6203 ParseArgs(StringGet, &p);
\r
6205 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6206 appData.noChessProgram = TRUE;
\r
6207 appData.icsActive = FALSE;
\r
6209 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6210 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6213 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6214 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6216 ParseArgs(StringGet, &p);
\r
6218 EndDialog(hDlg, TRUE);
\r
6225 case IDM_HELPCONTENTS:
\r
6226 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6227 MessageBox (GetFocus(),
\r
6228 _("Unable to activate help"),
\r
6229 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6234 SetStartupDialogEnables(hDlg);
\r
6242 /*---------------------------------------------------------------------------*\
\r
6244 * About box dialog functions
\r
6246 \*---------------------------------------------------------------------------*/
\r
6248 /* Process messages for "About" dialog box */
\r
6250 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6252 switch (message) {
\r
6253 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6254 /* Center the dialog over the application window */
\r
6255 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6256 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6257 Translate(hDlg, ABOUTBOX);
\r
6261 case WM_COMMAND: /* message: received a command */
\r
6262 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6263 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6264 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6272 /*---------------------------------------------------------------------------*\
\r
6274 * Comment Dialog functions
\r
6276 \*---------------------------------------------------------------------------*/
\r
6279 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6281 static HANDLE hwndText = NULL;
\r
6282 int len, newSizeX, newSizeY, flags;
\r
6283 static int sizeX, sizeY;
\r
6288 switch (message) {
\r
6289 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6290 /* Initialize the dialog items */
\r
6291 Translate(hDlg, DLG_EditComment);
\r
6292 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6293 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6294 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6295 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6296 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6297 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6298 SetWindowText(hDlg, commentTitle);
\r
6299 if (editComment) {
\r
6300 SetFocus(hwndText);
\r
6302 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6304 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6305 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6306 MAKELPARAM(FALSE, 0));
\r
6307 /* Size and position the dialog */
\r
6308 if (!commentDialog) {
\r
6309 commentDialog = hDlg;
\r
6310 flags = SWP_NOZORDER;
\r
6311 GetClientRect(hDlg, &rect);
\r
6312 sizeX = rect.right;
\r
6313 sizeY = rect.bottom;
\r
6314 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6315 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6316 WINDOWPLACEMENT wp;
\r
6317 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6318 wp.length = sizeof(WINDOWPLACEMENT);
\r
6320 wp.showCmd = SW_SHOW;
\r
6321 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6322 wp.rcNormalPosition.left = wpComment.x;
\r
6323 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6324 wp.rcNormalPosition.top = wpComment.y;
\r
6325 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6326 SetWindowPlacement(hDlg, &wp);
\r
6328 GetClientRect(hDlg, &rect);
\r
6329 newSizeX = rect.right;
\r
6330 newSizeY = rect.bottom;
\r
6331 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6332 newSizeX, newSizeY);
\r
6337 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6340 case WM_COMMAND: /* message: received a command */
\r
6341 switch (LOWORD(wParam)) {
\r
6343 if (editComment) {
\r
6345 /* Read changed options from the dialog box */
\r
6346 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6347 len = GetWindowTextLength(hwndText);
\r
6348 str = (char *) malloc(len + 1);
\r
6349 GetWindowText(hwndText, str, len + 1);
\r
6358 ReplaceComment(commentIndex, str);
\r
6365 case OPT_CancelComment:
\r
6369 case OPT_ClearComment:
\r
6370 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6373 case OPT_EditComment:
\r
6374 EditCommentEvent();
\r
6382 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6383 if( wParam == OPT_CommentText ) {
\r
6384 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6386 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6387 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6391 pt.x = LOWORD( lpMF->lParam );
\r
6392 pt.y = HIWORD( lpMF->lParam );
\r
6394 if(lpMF->msg == WM_CHAR) {
\r
6396 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6397 index = sel.cpMin;
\r
6399 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6401 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6402 len = GetWindowTextLength(hwndText);
\r
6403 str = (char *) malloc(len + 1);
\r
6404 GetWindowText(hwndText, str, len + 1);
\r
6405 ReplaceComment(commentIndex, str);
\r
6406 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6407 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6410 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6411 lpMF->msg = WM_USER;
\r
6419 newSizeX = LOWORD(lParam);
\r
6420 newSizeY = HIWORD(lParam);
\r
6421 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6426 case WM_GETMINMAXINFO:
\r
6427 /* Prevent resizing window too small */
\r
6428 mmi = (MINMAXINFO *) lParam;
\r
6429 mmi->ptMinTrackSize.x = 100;
\r
6430 mmi->ptMinTrackSize.y = 100;
\r
6437 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6442 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6444 if (str == NULL) str = "";
\r
6445 p = (char *) malloc(2 * strlen(str) + 2);
\r
6448 if (*str == '\n') *q++ = '\r';
\r
6452 if (commentText != NULL) free(commentText);
\r
6454 commentIndex = index;
\r
6455 commentTitle = title;
\r
6457 editComment = edit;
\r
6459 if (commentDialog) {
\r
6460 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6461 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6463 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6464 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6465 hwndMain, (DLGPROC)lpProc);
\r
6466 FreeProcInstance(lpProc);
\r
6472 /*---------------------------------------------------------------------------*\
\r
6474 * Type-in move dialog functions
\r
6476 \*---------------------------------------------------------------------------*/
\r
6479 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6481 char move[MSG_SIZ];
\r
6484 switch (message) {
\r
6485 case WM_INITDIALOG:
\r
6486 move[0] = (char) lParam;
\r
6487 move[1] = NULLCHAR;
\r
6488 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6489 Translate(hDlg, DLG_TypeInMove);
\r
6490 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6491 SetWindowText(hInput, move);
\r
6493 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6497 switch (LOWORD(wParam)) {
\r
6500 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6501 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6502 TypeInDoneEvent(move);
\r
6503 EndDialog(hDlg, TRUE);
\r
6506 EndDialog(hDlg, FALSE);
\r
6517 PopUpMoveDialog(char firstchar)
\r
6521 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6522 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6523 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6524 FreeProcInstance(lpProc);
\r
6527 /*---------------------------------------------------------------------------*\
\r
6529 * Type-in name dialog functions
\r
6531 \*---------------------------------------------------------------------------*/
\r
6534 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6536 char move[MSG_SIZ];
\r
6539 switch (message) {
\r
6540 case WM_INITDIALOG:
\r
6541 move[0] = (char) lParam;
\r
6542 move[1] = NULLCHAR;
\r
6543 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6544 Translate(hDlg, DLG_TypeInName);
\r
6545 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6546 SetWindowText(hInput, move);
\r
6548 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6552 switch (LOWORD(wParam)) {
\r
6554 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6555 appData.userName = strdup(move);
\r
6558 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6559 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6560 DisplayTitle(move);
\r
6564 EndDialog(hDlg, TRUE);
\r
6567 EndDialog(hDlg, FALSE);
\r
6578 PopUpNameDialog(char firstchar)
\r
6582 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6583 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6584 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6585 FreeProcInstance(lpProc);
\r
6588 /*---------------------------------------------------------------------------*\
\r
6592 \*---------------------------------------------------------------------------*/
\r
6594 /* Nonmodal error box */
\r
6595 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6596 WPARAM wParam, LPARAM lParam);
\r
6599 ErrorPopUp(char *title, char *content)
\r
6603 BOOLEAN modal = hwndMain == NULL;
\r
6621 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6622 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6625 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6627 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6628 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6629 hwndMain, (DLGPROC)lpProc);
\r
6630 FreeProcInstance(lpProc);
\r
6637 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6638 if (errorDialog == NULL) return;
\r
6639 DestroyWindow(errorDialog);
\r
6640 errorDialog = NULL;
\r
6641 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6645 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6650 switch (message) {
\r
6651 case WM_INITDIALOG:
\r
6652 GetWindowRect(hDlg, &rChild);
\r
6655 SetWindowPos(hDlg, NULL, rChild.left,
\r
6656 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6657 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6661 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6662 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6663 and it doesn't work when you resize the dialog.
\r
6664 For now, just give it a default position.
\r
6666 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6667 Translate(hDlg, DLG_Error);
\r
6669 errorDialog = hDlg;
\r
6670 SetWindowText(hDlg, errorTitle);
\r
6671 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6672 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6676 switch (LOWORD(wParam)) {
\r
6679 if (errorDialog == hDlg) errorDialog = NULL;
\r
6680 DestroyWindow(hDlg);
\r
6692 HWND gothicDialog = NULL;
\r
6695 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6699 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6701 switch (message) {
\r
6702 case WM_INITDIALOG:
\r
6703 GetWindowRect(hDlg, &rChild);
\r
6705 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6709 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6710 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6711 and it doesn't work when you resize the dialog.
\r
6712 For now, just give it a default position.
\r
6714 gothicDialog = hDlg;
\r
6715 SetWindowText(hDlg, errorTitle);
\r
6716 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6717 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6721 switch (LOWORD(wParam)) {
\r
6724 if (errorDialog == hDlg) errorDialog = NULL;
\r
6725 DestroyWindow(hDlg);
\r
6737 GothicPopUp(char *title, VariantClass variant)
\r
6740 static char *lastTitle;
\r
6742 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6743 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6745 if(lastTitle != title && gothicDialog != NULL) {
\r
6746 DestroyWindow(gothicDialog);
\r
6747 gothicDialog = NULL;
\r
6749 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6750 title = lastTitle;
\r
6751 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6752 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6753 hwndMain, (DLGPROC)lpProc);
\r
6754 FreeProcInstance(lpProc);
\r
6759 /*---------------------------------------------------------------------------*\
\r
6761 * Ics Interaction console functions
\r
6763 \*---------------------------------------------------------------------------*/
\r
6765 #define HISTORY_SIZE 64
\r
6766 static char *history[HISTORY_SIZE];
\r
6767 int histIn = 0, histP = 0;
\r
6770 SaveInHistory(char *cmd)
\r
6772 if (history[histIn] != NULL) {
\r
6773 free(history[histIn]);
\r
6774 history[histIn] = NULL;
\r
6776 if (*cmd == NULLCHAR) return;
\r
6777 history[histIn] = StrSave(cmd);
\r
6778 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6779 if (history[histIn] != NULL) {
\r
6780 free(history[histIn]);
\r
6781 history[histIn] = NULL;
\r
6787 PrevInHistory(char *cmd)
\r
6790 if (histP == histIn) {
\r
6791 if (history[histIn] != NULL) free(history[histIn]);
\r
6792 history[histIn] = StrSave(cmd);
\r
6794 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6795 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6797 return history[histP];
\r
6803 if (histP == histIn) return NULL;
\r
6804 histP = (histP + 1) % HISTORY_SIZE;
\r
6805 return history[histP];
\r
6809 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6813 hmenu = LoadMenu(hInst, "TextMenu");
\r
6814 h = GetSubMenu(hmenu, 0);
\r
6816 if (strcmp(e->item, "-") == 0) {
\r
6817 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6818 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6819 int flags = MF_STRING, j = 0;
\r
6820 if (e->item[0] == '|') {
\r
6821 flags |= MF_MENUBARBREAK;
\r
6824 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6825 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6833 WNDPROC consoleTextWindowProc;
\r
6836 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6838 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6839 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6843 SetWindowText(hInput, command);
\r
6845 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6847 sel.cpMin = 999999;
\r
6848 sel.cpMax = 999999;
\r
6849 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6854 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6855 if (sel.cpMin == sel.cpMax) {
\r
6856 /* Expand to surrounding word */
\r
6859 tr.chrg.cpMax = sel.cpMin;
\r
6860 tr.chrg.cpMin = --sel.cpMin;
\r
6861 if (sel.cpMin < 0) break;
\r
6862 tr.lpstrText = name;
\r
6863 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6864 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6868 tr.chrg.cpMin = sel.cpMax;
\r
6869 tr.chrg.cpMax = ++sel.cpMax;
\r
6870 tr.lpstrText = name;
\r
6871 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6872 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6875 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6876 MessageBeep(MB_ICONEXCLAMATION);
\r
6880 tr.lpstrText = name;
\r
6881 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6883 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6884 MessageBeep(MB_ICONEXCLAMATION);
\r
6887 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6890 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6891 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6892 SetWindowText(hInput, buf);
\r
6893 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6895 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6896 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6897 SetWindowText(hInput, buf);
\r
6898 sel.cpMin = 999999;
\r
6899 sel.cpMax = 999999;
\r
6900 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6906 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6911 switch (message) {
\r
6913 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6914 if(wParam=='R') return 0;
\r
6917 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6920 sel.cpMin = 999999;
\r
6921 sel.cpMax = 999999;
\r
6922 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6923 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6928 if(wParam != '\022') {
\r
6929 if (wParam == '\t') {
\r
6930 if (GetKeyState(VK_SHIFT) < 0) {
\r
6932 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6933 if (buttonDesc[0].hwnd) {
\r
6934 SetFocus(buttonDesc[0].hwnd);
\r
6936 SetFocus(hwndMain);
\r
6940 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6943 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6944 JAWS_DELETE( SetFocus(hInput); )
\r
6945 SendMessage(hInput, message, wParam, lParam);
\r
6948 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6950 case WM_RBUTTONDOWN:
\r
6951 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6952 /* Move selection here if it was empty */
\r
6954 pt.x = LOWORD(lParam);
\r
6955 pt.y = HIWORD(lParam);
\r
6956 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6957 if (sel.cpMin == sel.cpMax) {
\r
6958 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6959 sel.cpMax = sel.cpMin;
\r
6960 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6962 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6963 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6965 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6966 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6967 if (sel.cpMin == sel.cpMax) {
\r
6968 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6969 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6971 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6972 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6974 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6975 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6976 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6977 MenuPopup(hwnd, pt, hmenu, -1);
\r
6981 case WM_RBUTTONUP:
\r
6982 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6983 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6984 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6988 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6990 return SendMessage(hInput, message, wParam, lParam);
\r
6991 case WM_MBUTTONDOWN:
\r
6992 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6994 switch (LOWORD(wParam)) {
\r
6995 case IDM_QuickPaste:
\r
6997 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6998 if (sel.cpMin == sel.cpMax) {
\r
6999 MessageBeep(MB_ICONEXCLAMATION);
\r
7002 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7003 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7004 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7009 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7012 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7015 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7019 int i = LOWORD(wParam) - IDM_CommandX;
\r
7020 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7021 icsTextMenuEntry[i].command != NULL) {
\r
7022 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7023 icsTextMenuEntry[i].getname,
\r
7024 icsTextMenuEntry[i].immediate);
\r
7032 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7035 WNDPROC consoleInputWindowProc;
\r
7038 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7040 char buf[MSG_SIZ];
\r
7042 static BOOL sendNextChar = FALSE;
\r
7043 static BOOL quoteNextChar = FALSE;
\r
7044 InputSource *is = consoleInputSource;
\r
7048 switch (message) {
\r
7050 if (!appData.localLineEditing || sendNextChar) {
\r
7051 is->buf[0] = (CHAR) wParam;
\r
7053 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7054 sendNextChar = FALSE;
\r
7057 if (quoteNextChar) {
\r
7058 buf[0] = (char) wParam;
\r
7059 buf[1] = NULLCHAR;
\r
7060 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7061 quoteNextChar = FALSE;
\r
7065 case '\r': /* Enter key */
\r
7066 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7067 if (consoleEcho) SaveInHistory(is->buf);
\r
7068 is->buf[is->count++] = '\n';
\r
7069 is->buf[is->count] = NULLCHAR;
\r
7070 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7071 if (consoleEcho) {
\r
7072 ConsoleOutput(is->buf, is->count, TRUE);
\r
7073 } else if (appData.localLineEditing) {
\r
7074 ConsoleOutput("\n", 1, TRUE);
\r
7077 case '\033': /* Escape key */
\r
7078 SetWindowText(hwnd, "");
\r
7079 cf.cbSize = sizeof(CHARFORMAT);
\r
7080 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7081 if (consoleEcho) {
\r
7082 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7084 cf.crTextColor = COLOR_ECHOOFF;
\r
7086 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7087 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7089 case '\t': /* Tab key */
\r
7090 if (GetKeyState(VK_SHIFT) < 0) {
\r
7092 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7095 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7096 if (buttonDesc[0].hwnd) {
\r
7097 SetFocus(buttonDesc[0].hwnd);
\r
7099 SetFocus(hwndMain);
\r
7103 case '\023': /* Ctrl+S */
\r
7104 sendNextChar = TRUE;
\r
7106 case '\021': /* Ctrl+Q */
\r
7107 quoteNextChar = TRUE;
\r
7117 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7118 p = PrevInHistory(buf);
\r
7120 SetWindowText(hwnd, p);
\r
7121 sel.cpMin = 999999;
\r
7122 sel.cpMax = 999999;
\r
7123 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7128 p = NextInHistory();
\r
7130 SetWindowText(hwnd, p);
\r
7131 sel.cpMin = 999999;
\r
7132 sel.cpMax = 999999;
\r
7133 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7139 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7143 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7147 case WM_MBUTTONDOWN:
\r
7148 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7149 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7151 case WM_RBUTTONUP:
\r
7152 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7153 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7154 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7158 hmenu = LoadMenu(hInst, "InputMenu");
\r
7159 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7160 if (sel.cpMin == sel.cpMax) {
\r
7161 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7162 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7164 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7165 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7167 pt.x = LOWORD(lParam);
\r
7168 pt.y = HIWORD(lParam);
\r
7169 MenuPopup(hwnd, pt, hmenu, -1);
\r
7173 switch (LOWORD(wParam)) {
\r
7175 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7177 case IDM_SelectAll:
\r
7179 sel.cpMax = -1; /*999999?*/
\r
7180 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7183 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7186 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7189 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7194 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7197 #define CO_MAX 100000
\r
7198 #define CO_TRIM 1000
\r
7201 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7203 static SnapData sd;
\r
7204 HWND hText, hInput;
\r
7206 static int sizeX, sizeY;
\r
7207 int newSizeX, newSizeY;
\r
7211 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7212 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7214 switch (message) {
\r
7216 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7218 ENLINK *pLink = (ENLINK*)lParam;
\r
7219 if (pLink->msg == WM_LBUTTONUP)
\r
7223 tr.chrg = pLink->chrg;
\r
7224 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7225 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7226 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7227 free(tr.lpstrText);
\r
7231 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7232 hwndConsole = hDlg;
\r
7234 consoleTextWindowProc = (WNDPROC)
\r
7235 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7236 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7237 consoleInputWindowProc = (WNDPROC)
\r
7238 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7239 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7240 Colorize(ColorNormal, TRUE);
\r
7241 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7242 ChangedConsoleFont();
\r
7243 GetClientRect(hDlg, &rect);
\r
7244 sizeX = rect.right;
\r
7245 sizeY = rect.bottom;
\r
7246 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7247 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7248 WINDOWPLACEMENT wp;
\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 // [HGM] Chessknight's change 2004-07-13
\r
7262 else { /* Determine Defaults */
\r
7263 WINDOWPLACEMENT wp;
\r
7264 wpConsole.x = wpMain.width + 1;
\r
7265 wpConsole.y = wpMain.y;
\r
7266 wpConsole.width = screenWidth - wpMain.width;
\r
7267 wpConsole.height = wpMain.height;
\r
7268 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7269 wp.length = sizeof(WINDOWPLACEMENT);
\r
7271 wp.showCmd = SW_SHOW;
\r
7272 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7273 wp.rcNormalPosition.left = wpConsole.x;
\r
7274 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7275 wp.rcNormalPosition.top = wpConsole.y;
\r
7276 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7277 SetWindowPlacement(hDlg, &wp);
\r
7280 // Allow hText to highlight URLs and send notifications on them
\r
7281 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7282 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7283 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7284 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7298 if (IsIconic(hDlg)) break;
\r
7299 newSizeX = LOWORD(lParam);
\r
7300 newSizeY = HIWORD(lParam);
\r
7301 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7302 RECT rectText, rectInput;
\r
7304 int newTextHeight, newTextWidth;
\r
7305 GetWindowRect(hText, &rectText);
\r
7306 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7307 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7308 if (newTextHeight < 0) {
\r
7309 newSizeY += -newTextHeight;
\r
7310 newTextHeight = 0;
\r
7312 SetWindowPos(hText, NULL, 0, 0,
\r
7313 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7314 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7315 pt.x = rectInput.left;
\r
7316 pt.y = rectInput.top + newSizeY - sizeY;
\r
7317 ScreenToClient(hDlg, &pt);
\r
7318 SetWindowPos(hInput, NULL,
\r
7319 pt.x, pt.y, /* needs client coords */
\r
7320 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7321 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7327 case WM_GETMINMAXINFO:
\r
7328 /* Prevent resizing window too small */
\r
7329 mmi = (MINMAXINFO *) lParam;
\r
7330 mmi->ptMinTrackSize.x = 100;
\r
7331 mmi->ptMinTrackSize.y = 100;
\r
7334 /* [AS] Snapping */
\r
7335 case WM_ENTERSIZEMOVE:
\r
7336 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7339 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7342 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7344 case WM_EXITSIZEMOVE:
\r
7345 UpdateICSWidth(hText);
\r
7346 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7349 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7357 if (hwndConsole) return;
\r
7358 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7359 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7364 ConsoleOutput(char* data, int length, int forceVisible)
\r
7369 char buf[CO_MAX+1];
\r
7372 static int delayLF = 0;
\r
7373 CHARRANGE savesel, sel;
\r
7375 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7383 while (length--) {
\r
7391 } else if (*p == '\007') {
\r
7392 MyPlaySound(&sounds[(int)SoundBell]);
\r
7399 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7400 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7401 /* Save current selection */
\r
7402 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7403 exlen = GetWindowTextLength(hText);
\r
7404 /* Find out whether current end of text is visible */
\r
7405 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7406 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7407 /* Trim existing text if it's too long */
\r
7408 if (exlen + (q - buf) > CO_MAX) {
\r
7409 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7412 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7413 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7415 savesel.cpMin -= trim;
\r
7416 savesel.cpMax -= trim;
\r
7417 if (exlen < 0) exlen = 0;
\r
7418 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7419 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7421 /* Append the new text */
\r
7422 sel.cpMin = exlen;
\r
7423 sel.cpMax = exlen;
\r
7424 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7425 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7426 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7427 if (forceVisible || exlen == 0 ||
\r
7428 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7429 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7430 /* Scroll to make new end of text visible if old end of text
\r
7431 was visible or new text is an echo of user typein */
\r
7432 sel.cpMin = 9999999;
\r
7433 sel.cpMax = 9999999;
\r
7434 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7435 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7436 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7437 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7439 if (savesel.cpMax == exlen || forceVisible) {
\r
7440 /* Move insert point to new end of text if it was at the old
\r
7441 end of text or if the new text is an echo of user typein */
\r
7442 sel.cpMin = 9999999;
\r
7443 sel.cpMax = 9999999;
\r
7444 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7446 /* Restore previous selection */
\r
7447 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7449 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7456 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7460 COLORREF oldFg, oldBg;
\r
7464 if(copyNumber > 1)
\r
7465 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7467 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7468 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7469 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7472 rect.right = x + squareSize;
\r
7474 rect.bottom = y + squareSize;
\r
7477 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7478 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7479 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7480 &rect, str, strlen(str), NULL);
\r
7482 (void) SetTextColor(hdc, oldFg);
\r
7483 (void) SetBkColor(hdc, oldBg);
\r
7484 (void) SelectObject(hdc, oldFont);
\r
7488 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7489 RECT *rect, char *color, char *flagFell)
\r
7493 COLORREF oldFg, oldBg;
\r
7496 if (appData.clockMode) {
\r
7498 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7500 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7507 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7508 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7510 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7511 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7513 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7517 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7518 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7519 rect, str, strlen(str), NULL);
\r
7520 if(logoHeight > 0 && appData.clockMode) {
\r
7522 str += strlen(color)+2;
\r
7523 r.top = rect->top + logoHeight/2;
\r
7524 r.left = rect->left;
\r
7525 r.right = rect->right;
\r
7526 r.bottom = rect->bottom;
\r
7527 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7528 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7529 &r, str, strlen(str), NULL);
\r
7531 (void) SetTextColor(hdc, oldFg);
\r
7532 (void) SetBkColor(hdc, oldBg);
\r
7533 (void) SelectObject(hdc, oldFont);
\r
7538 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7544 if( count <= 0 ) {
\r
7545 if (appData.debugMode) {
\r
7546 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7549 return ERROR_INVALID_USER_BUFFER;
\r
7552 ResetEvent(ovl->hEvent);
\r
7553 ovl->Offset = ovl->OffsetHigh = 0;
\r
7554 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7558 err = GetLastError();
\r
7559 if (err == ERROR_IO_PENDING) {
\r
7560 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7564 err = GetLastError();
\r
7571 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7576 ResetEvent(ovl->hEvent);
\r
7577 ovl->Offset = ovl->OffsetHigh = 0;
\r
7578 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7582 err = GetLastError();
\r
7583 if (err == ERROR_IO_PENDING) {
\r
7584 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7588 err = GetLastError();
\r
7594 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7595 void CheckForInputBufferFull( InputSource * is )
\r
7597 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7598 /* Look for end of line */
\r
7599 char * p = is->buf;
\r
7601 while( p < is->next && *p != '\n' ) {
\r
7605 if( p >= is->next ) {
\r
7606 if (appData.debugMode) {
\r
7607 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7610 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7611 is->count = (DWORD) -1;
\r
7612 is->next = is->buf;
\r
7618 InputThread(LPVOID arg)
\r
7623 is = (InputSource *) arg;
\r
7624 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7625 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7626 while (is->hThread != NULL) {
\r
7627 is->error = DoReadFile(is->hFile, is->next,
\r
7628 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7629 &is->count, &ovl);
\r
7630 if (is->error == NO_ERROR) {
\r
7631 is->next += is->count;
\r
7633 if (is->error == ERROR_BROKEN_PIPE) {
\r
7634 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7637 is->count = (DWORD) -1;
\r
7638 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7643 CheckForInputBufferFull( is );
\r
7645 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7647 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7649 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7652 CloseHandle(ovl.hEvent);
\r
7653 CloseHandle(is->hFile);
\r
7655 if (appData.debugMode) {
\r
7656 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7663 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7665 NonOvlInputThread(LPVOID arg)
\r
7672 is = (InputSource *) arg;
\r
7673 while (is->hThread != NULL) {
\r
7674 is->error = ReadFile(is->hFile, is->next,
\r
7675 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7676 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7677 if (is->error == NO_ERROR) {
\r
7678 /* Change CRLF to LF */
\r
7679 if (is->next > is->buf) {
\r
7681 i = is->count + 1;
\r
7689 if (prev == '\r' && *p == '\n') {
\r
7701 if (is->error == ERROR_BROKEN_PIPE) {
\r
7702 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7705 is->count = (DWORD) -1;
\r
7709 CheckForInputBufferFull( is );
\r
7711 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7713 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7715 if (is->count < 0) break; /* Quit on error */
\r
7717 CloseHandle(is->hFile);
\r
7722 SocketInputThread(LPVOID arg)
\r
7726 is = (InputSource *) arg;
\r
7727 while (is->hThread != NULL) {
\r
7728 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7729 if ((int)is->count == SOCKET_ERROR) {
\r
7730 is->count = (DWORD) -1;
\r
7731 is->error = WSAGetLastError();
\r
7733 is->error = NO_ERROR;
\r
7734 is->next += is->count;
\r
7735 if (is->count == 0 && is->second == is) {
\r
7736 /* End of file on stderr; quit with no message */
\r
7740 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7742 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7744 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7750 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7754 is = (InputSource *) lParam;
\r
7755 if (is->lineByLine) {
\r
7756 /* Feed in lines one by one */
\r
7757 char *p = is->buf;
\r
7759 while (q < is->next) {
\r
7760 if (*q++ == '\n') {
\r
7761 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7766 /* Move any partial line to the start of the buffer */
\r
7768 while (p < is->next) {
\r
7773 if (is->error != NO_ERROR || is->count == 0) {
\r
7774 /* Notify backend of the error. Note: If there was a partial
\r
7775 line at the end, it is not flushed through. */
\r
7776 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7779 /* Feed in the whole chunk of input at once */
\r
7780 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7781 is->next = is->buf;
\r
7785 /*---------------------------------------------------------------------------*\
\r
7787 * Menu enables. Used when setting various modes.
\r
7789 \*---------------------------------------------------------------------------*/
\r
7797 GreyRevert(Boolean grey)
\r
7798 { // [HGM] vari: for retracting variations in local mode
\r
7799 HMENU hmenu = GetMenu(hwndMain);
\r
7800 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7801 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7805 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7807 while (enab->item > 0) {
\r
7808 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7813 Enables gnuEnables[] = {
\r
7814 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7828 // Needed to switch from ncp to GNU mode on Engine Load
\r
7829 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7830 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7831 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7832 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7833 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7834 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7835 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7836 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7837 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7838 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7840 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7841 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7842 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7846 Enables icsEnables[] = {
\r
7847 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7870 Enables zippyEnables[] = {
\r
7871 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7879 Enables ncpEnables[] = {
\r
7880 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7890 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7905 Enables trainingOnEnables[] = {
\r
7906 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7918 Enables trainingOffEnables[] = {
\r
7919 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7931 /* These modify either ncpEnables or gnuEnables */
\r
7932 Enables cmailEnables[] = {
\r
7933 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7936 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7943 Enables machineThinkingEnables[] = {
\r
7944 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7957 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7963 Enables userThinkingEnables[] = {
\r
7964 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7965 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7966 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7967 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7968 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7969 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7970 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7971 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7972 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7973 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7975 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7976 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7977 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7978 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7983 /*---------------------------------------------------------------------------*\
\r
7985 * Front-end interface functions exported by XBoard.
\r
7986 * Functions appear in same order as prototypes in frontend.h.
\r
7988 \*---------------------------------------------------------------------------*/
\r
7990 CheckMark(UINT item, int state)
\r
7992 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7998 static UINT prevChecked = 0;
\r
7999 static int prevPausing = 0;
\r
8002 if (pausing != prevPausing) {
\r
8003 prevPausing = pausing;
\r
8004 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8005 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8006 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8009 switch (gameMode) {
\r
8010 case BeginningOfGame:
\r
8011 if (appData.icsActive)
\r
8012 nowChecked = IDM_IcsClient;
\r
8013 else if (appData.noChessProgram)
\r
8014 nowChecked = IDM_EditGame;
\r
8016 nowChecked = IDM_MachineBlack;
\r
8018 case MachinePlaysBlack:
\r
8019 nowChecked = IDM_MachineBlack;
\r
8021 case MachinePlaysWhite:
\r
8022 nowChecked = IDM_MachineWhite;
\r
8024 case TwoMachinesPlay:
\r
8025 nowChecked = IDM_TwoMachines;
\r
8028 nowChecked = IDM_AnalysisMode;
\r
8031 nowChecked = IDM_AnalyzeFile;
\r
8034 nowChecked = IDM_EditGame;
\r
8036 case PlayFromGameFile:
\r
8037 nowChecked = IDM_LoadGame;
\r
8039 case EditPosition:
\r
8040 nowChecked = IDM_EditPosition;
\r
8043 nowChecked = IDM_Training;
\r
8045 case IcsPlayingWhite:
\r
8046 case IcsPlayingBlack:
\r
8047 case IcsObserving:
\r
8049 nowChecked = IDM_IcsClient;
\r
8056 CheckMark(prevChecked, MF_UNCHECKED);
\r
8057 CheckMark(nowChecked, MF_CHECKED);
\r
8058 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8060 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8061 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8062 MF_BYCOMMAND|MF_ENABLED);
\r
8064 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8065 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8068 prevChecked = nowChecked;
\r
8070 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8071 if (appData.icsActive) {
\r
8072 if (appData.icsEngineAnalyze) {
\r
8073 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8075 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8078 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8084 HMENU hmenu = GetMenu(hwndMain);
\r
8085 SetMenuEnables(hmenu, icsEnables);
\r
8086 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8087 MF_BYCOMMAND|MF_ENABLED);
\r
8089 if (appData.zippyPlay) {
\r
8090 SetMenuEnables(hmenu, zippyEnables);
\r
8091 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8092 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8093 MF_BYCOMMAND|MF_ENABLED);
\r
8101 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8107 HMENU hmenu = GetMenu(hwndMain);
\r
8108 SetMenuEnables(hmenu, ncpEnables);
\r
8109 DrawMenuBar(hwndMain);
\r
8115 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8119 SetTrainingModeOn()
\r
8122 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8123 for (i = 0; i < N_BUTTONS; i++) {
\r
8124 if (buttonDesc[i].hwnd != NULL)
\r
8125 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8130 VOID SetTrainingModeOff()
\r
8133 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8134 for (i = 0; i < N_BUTTONS; i++) {
\r
8135 if (buttonDesc[i].hwnd != NULL)
\r
8136 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8142 SetUserThinkingEnables()
\r
8144 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8148 SetMachineThinkingEnables()
\r
8150 HMENU hMenu = GetMenu(hwndMain);
\r
8151 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8153 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8155 if (gameMode == MachinePlaysBlack) {
\r
8156 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8157 } else if (gameMode == MachinePlaysWhite) {
\r
8158 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8159 } else if (gameMode == TwoMachinesPlay) {
\r
8160 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8166 DisplayTitle(char *str)
\r
8168 char title[MSG_SIZ], *host;
\r
8169 if (str[0] != NULLCHAR) {
\r
8170 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8171 } else if (appData.icsActive) {
\r
8172 if (appData.icsCommPort[0] != NULLCHAR)
\r
8175 host = appData.icsHost;
\r
8176 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8177 } else if (appData.noChessProgram) {
\r
8178 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8180 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8181 strcat(title, ": ");
\r
8182 strcat(title, first.tidy);
\r
8184 SetWindowText(hwndMain, title);
\r
8189 DisplayMessage(char *str1, char *str2)
\r
8193 int remain = MESSAGE_TEXT_MAX - 1;
\r
8196 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8197 messageText[0] = NULLCHAR;
\r
8199 len = strlen(str1);
\r
8200 if (len > remain) len = remain;
\r
8201 strncpy(messageText, str1, len);
\r
8202 messageText[len] = NULLCHAR;
\r
8205 if (*str2 && remain >= 2) {
\r
8207 strcat(messageText, " ");
\r
8210 len = strlen(str2);
\r
8211 if (len > remain) len = remain;
\r
8212 strncat(messageText, str2, len);
\r
8214 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8215 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8217 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8221 hdc = GetDC(hwndMain);
\r
8222 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8223 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8224 &messageRect, messageText, strlen(messageText), NULL);
\r
8225 (void) SelectObject(hdc, oldFont);
\r
8226 (void) ReleaseDC(hwndMain, hdc);
\r
8230 DisplayError(char *str, int error)
\r
8232 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8236 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8238 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8239 NULL, error, LANG_NEUTRAL,
\r
8240 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8242 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8244 ErrorMap *em = errmap;
\r
8245 while (em->err != 0 && em->err != error) em++;
\r
8246 if (em->err != 0) {
\r
8247 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8249 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8254 ErrorPopUp(_("Error"), buf);
\r
8259 DisplayMoveError(char *str)
\r
8261 fromX = fromY = -1;
\r
8262 ClearHighlights();
\r
8263 DrawPosition(FALSE, NULL);
\r
8264 if (appData.popupMoveErrors) {
\r
8265 ErrorPopUp(_("Error"), str);
\r
8267 DisplayMessage(str, "");
\r
8268 moveErrorMessageUp = TRUE;
\r
8273 DisplayFatalError(char *str, int error, int exitStatus)
\r
8275 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8277 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8280 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8281 NULL, error, LANG_NEUTRAL,
\r
8282 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8284 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8286 ErrorMap *em = errmap;
\r
8287 while (em->err != 0 && em->err != error) em++;
\r
8288 if (em->err != 0) {
\r
8289 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8291 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8296 if (appData.debugMode) {
\r
8297 fprintf(debugFP, "%s: %s\n", label, str);
\r
8299 if (appData.popupExitMessage) {
\r
8300 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8301 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8303 ExitEvent(exitStatus);
\r
8308 DisplayInformation(char *str)
\r
8310 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8315 DisplayNote(char *str)
\r
8317 ErrorPopUp(_("Note"), str);
\r
8322 char *title, *question, *replyPrefix;
\r
8327 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8329 static QuestionParams *qp;
\r
8330 char reply[MSG_SIZ];
\r
8333 switch (message) {
\r
8334 case WM_INITDIALOG:
\r
8335 qp = (QuestionParams *) lParam;
\r
8336 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8337 Translate(hDlg, DLG_Question);
\r
8338 SetWindowText(hDlg, qp->title);
\r
8339 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8340 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8344 switch (LOWORD(wParam)) {
\r
8346 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8347 if (*reply) strcat(reply, " ");
\r
8348 len = strlen(reply);
\r
8349 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8350 strcat(reply, "\n");
\r
8351 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8352 EndDialog(hDlg, TRUE);
\r
8353 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8356 EndDialog(hDlg, FALSE);
\r
8367 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8369 QuestionParams qp;
\r
8373 qp.question = question;
\r
8374 qp.replyPrefix = replyPrefix;
\r
8376 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8377 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8378 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8379 FreeProcInstance(lpProc);
\r
8382 /* [AS] Pick FRC position */
\r
8383 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8385 static int * lpIndexFRC;
\r
8391 case WM_INITDIALOG:
\r
8392 lpIndexFRC = (int *) lParam;
\r
8394 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8395 Translate(hDlg, DLG_NewGameFRC);
\r
8397 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8398 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8399 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8400 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8405 switch( LOWORD(wParam) ) {
\r
8407 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8408 EndDialog( hDlg, 0 );
\r
8409 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8412 EndDialog( hDlg, 1 );
\r
8414 case IDC_NFG_Edit:
\r
8415 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8416 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8418 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8421 case IDC_NFG_Random:
\r
8422 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8423 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8436 int index = appData.defaultFrcPosition;
\r
8437 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8439 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8441 if( result == 0 ) {
\r
8442 appData.defaultFrcPosition = index;
\r
8448 /* [AS] Game list options. Refactored by HGM */
\r
8450 HWND gameListOptionsDialog;
\r
8452 // low-level front-end: clear text edit / list widget
\r
8456 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8459 // low-level front-end: clear text edit / list widget
\r
8461 GLT_DeSelectList()
\r
8463 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8466 // low-level front-end: append line to text edit / list widget
\r
8468 GLT_AddToList( char *name )
\r
8471 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8475 // low-level front-end: get line from text edit / list widget
\r
8477 GLT_GetFromList( int index, char *name )
\r
8480 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8486 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8488 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8489 int idx2 = idx1 + delta;
\r
8490 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8492 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8495 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8496 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8497 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8498 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8502 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8506 case WM_INITDIALOG:
\r
8507 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8509 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8510 Translate(hDlg, DLG_GameListOptions);
\r
8512 /* Initialize list */
\r
8513 GLT_TagsToList( lpUserGLT );
\r
8515 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8520 switch( LOWORD(wParam) ) {
\r
8523 EndDialog( hDlg, 0 );
\r
8526 EndDialog( hDlg, 1 );
\r
8529 case IDC_GLT_Default:
\r
8530 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8533 case IDC_GLT_Restore:
\r
8534 GLT_TagsToList( appData.gameListTags );
\r
8538 GLT_MoveSelection( hDlg, -1 );
\r
8541 case IDC_GLT_Down:
\r
8542 GLT_MoveSelection( hDlg, +1 );
\r
8552 int GameListOptions()
\r
8555 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8557 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8559 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8561 if( result == 0 ) {
\r
8562 /* [AS] Memory leak here! */
\r
8563 appData.gameListTags = strdup( lpUserGLT );
\r
8570 DisplayIcsInteractionTitle(char *str)
\r
8572 char consoleTitle[MSG_SIZ];
\r
8574 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8575 SetWindowText(hwndConsole, consoleTitle);
\r
8577 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8578 char buf[MSG_SIZ], *p = buf, *q;
\r
8579 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8581 q = strchr(p, ';');
\r
8583 if(*p) ChatPopUp(p);
\r
8587 SetActiveWindow(hwndMain);
\r
8591 DrawPosition(int fullRedraw, Board board)
\r
8593 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8596 void NotifyFrontendLogin()
\r
8599 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8605 fromX = fromY = -1;
\r
8606 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8607 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8608 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8609 dragInfo.lastpos = dragInfo.pos;
\r
8610 dragInfo.start.x = dragInfo.start.y = -1;
\r
8611 dragInfo.from = dragInfo.start;
\r
8613 DrawPosition(TRUE, NULL);
\r
8620 CommentPopUp(char *title, char *str)
\r
8622 HWND hwnd = GetActiveWindow();
\r
8623 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8625 SetActiveWindow(hwnd);
\r
8629 CommentPopDown(void)
\r
8631 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8632 if (commentDialog) {
\r
8633 ShowWindow(commentDialog, SW_HIDE);
\r
8635 commentUp = FALSE;
\r
8639 EditCommentPopUp(int index, char *title, char *str)
\r
8641 EitherCommentPopUp(index, title, str, TRUE);
\r
8648 MyPlaySound(&sounds[(int)SoundMove]);
\r
8651 VOID PlayIcsWinSound()
\r
8653 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8656 VOID PlayIcsLossSound()
\r
8658 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8661 VOID PlayIcsDrawSound()
\r
8663 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8666 VOID PlayIcsUnfinishedSound()
\r
8668 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8674 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8680 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8688 consoleEcho = TRUE;
\r
8689 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8690 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8691 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8700 consoleEcho = FALSE;
\r
8701 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8702 /* This works OK: set text and background both to the same color */
\r
8704 cf.crTextColor = COLOR_ECHOOFF;
\r
8705 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8706 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8709 /* No Raw()...? */
\r
8711 void Colorize(ColorClass cc, int continuation)
\r
8713 currentColorClass = cc;
\r
8714 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8715 consoleCF.crTextColor = textAttribs[cc].color;
\r
8716 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8717 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8723 static char buf[MSG_SIZ];
\r
8724 DWORD bufsiz = MSG_SIZ;
\r
8726 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8727 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8729 if (!GetUserName(buf, &bufsiz)) {
\r
8730 /*DisplayError("Error getting user name", GetLastError());*/
\r
8731 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8739 static char buf[MSG_SIZ];
\r
8740 DWORD bufsiz = MSG_SIZ;
\r
8742 if (!GetComputerName(buf, &bufsiz)) {
\r
8743 /*DisplayError("Error getting host name", GetLastError());*/
\r
8744 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8751 ClockTimerRunning()
\r
8753 return clockTimerEvent != 0;
\r
8759 if (clockTimerEvent == 0) return FALSE;
\r
8760 KillTimer(hwndMain, clockTimerEvent);
\r
8761 clockTimerEvent = 0;
\r
8766 StartClockTimer(long millisec)
\r
8768 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8769 (UINT) millisec, NULL);
\r
8773 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8776 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8778 if(appData.noGUI) return;
\r
8779 hdc = GetDC(hwndMain);
\r
8780 if (!IsIconic(hwndMain)) {
\r
8781 DisplayAClock(hdc, timeRemaining, highlight,
\r
8782 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8784 if (highlight && iconCurrent == iconBlack) {
\r
8785 iconCurrent = iconWhite;
\r
8786 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8787 if (IsIconic(hwndMain)) {
\r
8788 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8791 (void) ReleaseDC(hwndMain, hdc);
\r
8793 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8797 DisplayBlackClock(long timeRemaining, int highlight)
\r
8800 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8802 if(appData.noGUI) return;
\r
8803 hdc = GetDC(hwndMain);
\r
8804 if (!IsIconic(hwndMain)) {
\r
8805 DisplayAClock(hdc, timeRemaining, highlight,
\r
8806 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8808 if (highlight && iconCurrent == iconWhite) {
\r
8809 iconCurrent = iconBlack;
\r
8810 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8811 if (IsIconic(hwndMain)) {
\r
8812 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8815 (void) ReleaseDC(hwndMain, hdc);
\r
8817 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8822 LoadGameTimerRunning()
\r
8824 return loadGameTimerEvent != 0;
\r
8828 StopLoadGameTimer()
\r
8830 if (loadGameTimerEvent == 0) return FALSE;
\r
8831 KillTimer(hwndMain, loadGameTimerEvent);
\r
8832 loadGameTimerEvent = 0;
\r
8837 StartLoadGameTimer(long millisec)
\r
8839 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8840 (UINT) millisec, NULL);
\r
8848 char fileTitle[MSG_SIZ];
\r
8850 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8851 f = OpenFileDialog(hwndMain, "a", defName,
\r
8852 appData.oldSaveStyle ? "gam" : "pgn",
\r
8854 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8856 SaveGame(f, 0, "");
\r
8863 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8865 if (delayedTimerEvent != 0) {
\r
8866 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8867 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8869 KillTimer(hwndMain, delayedTimerEvent);
\r
8870 delayedTimerEvent = 0;
\r
8871 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8872 delayedTimerCallback();
\r
8874 delayedTimerCallback = cb;
\r
8875 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8876 (UINT) millisec, NULL);
\r
8879 DelayedEventCallback
\r
8882 if (delayedTimerEvent) {
\r
8883 return delayedTimerCallback;
\r
8890 CancelDelayedEvent()
\r
8892 if (delayedTimerEvent) {
\r
8893 KillTimer(hwndMain, delayedTimerEvent);
\r
8894 delayedTimerEvent = 0;
\r
8898 DWORD GetWin32Priority(int nice)
\r
8899 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8901 REALTIME_PRIORITY_CLASS 0x00000100
\r
8902 HIGH_PRIORITY_CLASS 0x00000080
\r
8903 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8904 NORMAL_PRIORITY_CLASS 0x00000020
\r
8905 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8906 IDLE_PRIORITY_CLASS 0x00000040
\r
8908 if (nice < -15) return 0x00000080;
\r
8909 if (nice < 0) return 0x00008000;
\r
8910 if (nice == 0) return 0x00000020;
\r
8911 if (nice < 15) return 0x00004000;
\r
8912 return 0x00000040;
\r
8915 void RunCommand(char *cmdLine)
\r
8917 /* Now create the child process. */
\r
8918 STARTUPINFO siStartInfo;
\r
8919 PROCESS_INFORMATION piProcInfo;
\r
8921 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8922 siStartInfo.lpReserved = NULL;
\r
8923 siStartInfo.lpDesktop = NULL;
\r
8924 siStartInfo.lpTitle = NULL;
\r
8925 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8926 siStartInfo.cbReserved2 = 0;
\r
8927 siStartInfo.lpReserved2 = NULL;
\r
8928 siStartInfo.hStdInput = NULL;
\r
8929 siStartInfo.hStdOutput = NULL;
\r
8930 siStartInfo.hStdError = NULL;
\r
8932 CreateProcess(NULL,
\r
8933 cmdLine, /* command line */
\r
8934 NULL, /* process security attributes */
\r
8935 NULL, /* primary thread security attrs */
\r
8936 TRUE, /* handles are inherited */
\r
8937 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8938 NULL, /* use parent's environment */
\r
8940 &siStartInfo, /* STARTUPINFO pointer */
\r
8941 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8943 CloseHandle(piProcInfo.hThread);
\r
8946 /* Start a child process running the given program.
\r
8947 The process's standard output can be read from "from", and its
\r
8948 standard input can be written to "to".
\r
8949 Exit with fatal error if anything goes wrong.
\r
8950 Returns an opaque pointer that can be used to destroy the process
\r
8954 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8956 #define BUFSIZE 4096
\r
8958 HANDLE hChildStdinRd, hChildStdinWr,
\r
8959 hChildStdoutRd, hChildStdoutWr;
\r
8960 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8961 SECURITY_ATTRIBUTES saAttr;
\r
8963 PROCESS_INFORMATION piProcInfo;
\r
8964 STARTUPINFO siStartInfo;
\r
8966 char buf[MSG_SIZ];
\r
8969 if (appData.debugMode) {
\r
8970 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8975 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8976 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8977 saAttr.bInheritHandle = TRUE;
\r
8978 saAttr.lpSecurityDescriptor = NULL;
\r
8981 * The steps for redirecting child's STDOUT:
\r
8982 * 1. Create anonymous pipe to be STDOUT for child.
\r
8983 * 2. Create a noninheritable duplicate of read handle,
\r
8984 * and close the inheritable read handle.
\r
8987 /* Create a pipe for the child's STDOUT. */
\r
8988 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8989 return GetLastError();
\r
8992 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8993 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8994 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8995 FALSE, /* not inherited */
\r
8996 DUPLICATE_SAME_ACCESS);
\r
8998 return GetLastError();
\r
9000 CloseHandle(hChildStdoutRd);
\r
9003 * The steps for redirecting child's STDIN:
\r
9004 * 1. Create anonymous pipe to be STDIN for child.
\r
9005 * 2. Create a noninheritable duplicate of write handle,
\r
9006 * and close the inheritable write handle.
\r
9009 /* Create a pipe for the child's STDIN. */
\r
9010 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9011 return GetLastError();
\r
9014 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9015 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9016 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9017 FALSE, /* not inherited */
\r
9018 DUPLICATE_SAME_ACCESS);
\r
9020 return GetLastError();
\r
9022 CloseHandle(hChildStdinWr);
\r
9024 /* Arrange to (1) look in dir for the child .exe file, and
\r
9025 * (2) have dir be the child's working directory. Interpret
\r
9026 * dir relative to the directory WinBoard loaded from. */
\r
9027 GetCurrentDirectory(MSG_SIZ, buf);
\r
9028 SetCurrentDirectory(installDir);
\r
9029 SetCurrentDirectory(dir);
\r
9031 /* Now create the child process. */
\r
9033 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9034 siStartInfo.lpReserved = NULL;
\r
9035 siStartInfo.lpDesktop = NULL;
\r
9036 siStartInfo.lpTitle = NULL;
\r
9037 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9038 siStartInfo.cbReserved2 = 0;
\r
9039 siStartInfo.lpReserved2 = NULL;
\r
9040 siStartInfo.hStdInput = hChildStdinRd;
\r
9041 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9042 siStartInfo.hStdError = hChildStdoutWr;
\r
9044 fSuccess = CreateProcess(NULL,
\r
9045 cmdLine, /* command line */
\r
9046 NULL, /* process security attributes */
\r
9047 NULL, /* primary thread security attrs */
\r
9048 TRUE, /* handles are inherited */
\r
9049 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9050 NULL, /* use parent's environment */
\r
9052 &siStartInfo, /* STARTUPINFO pointer */
\r
9053 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9055 err = GetLastError();
\r
9056 SetCurrentDirectory(buf); /* return to prev directory */
\r
9061 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9062 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9063 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9066 /* Close the handles we don't need in the parent */
\r
9067 CloseHandle(piProcInfo.hThread);
\r
9068 CloseHandle(hChildStdinRd);
\r
9069 CloseHandle(hChildStdoutWr);
\r
9071 /* Prepare return value */
\r
9072 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9073 cp->kind = CPReal;
\r
9074 cp->hProcess = piProcInfo.hProcess;
\r
9075 cp->pid = piProcInfo.dwProcessId;
\r
9076 cp->hFrom = hChildStdoutRdDup;
\r
9077 cp->hTo = hChildStdinWrDup;
\r
9079 *pr = (void *) cp;
\r
9081 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9082 2000 where engines sometimes don't see the initial command(s)
\r
9083 from WinBoard and hang. I don't understand how that can happen,
\r
9084 but the Sleep is harmless, so I've put it in. Others have also
\r
9085 reported what may be the same problem, so hopefully this will fix
\r
9086 it for them too. */
\r
9094 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9096 ChildProc *cp; int result;
\r
9098 cp = (ChildProc *) pr;
\r
9099 if (cp == NULL) return;
\r
9101 switch (cp->kind) {
\r
9103 /* TerminateProcess is considered harmful, so... */
\r
9104 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9105 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9106 /* The following doesn't work because the chess program
\r
9107 doesn't "have the same console" as WinBoard. Maybe
\r
9108 we could arrange for this even though neither WinBoard
\r
9109 nor the chess program uses a console for stdio? */
\r
9110 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9112 /* [AS] Special termination modes for misbehaving programs... */
\r
9113 if( signal == 9 ) {
\r
9114 result = TerminateProcess( cp->hProcess, 0 );
\r
9116 if ( appData.debugMode) {
\r
9117 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9120 else if( signal == 10 ) {
\r
9121 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9123 if( dw != WAIT_OBJECT_0 ) {
\r
9124 result = TerminateProcess( cp->hProcess, 0 );
\r
9126 if ( appData.debugMode) {
\r
9127 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9133 CloseHandle(cp->hProcess);
\r
9137 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9141 closesocket(cp->sock);
\r
9146 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9147 closesocket(cp->sock);
\r
9148 closesocket(cp->sock2);
\r
9156 InterruptChildProcess(ProcRef pr)
\r
9160 cp = (ChildProc *) pr;
\r
9161 if (cp == NULL) return;
\r
9162 switch (cp->kind) {
\r
9164 /* The following doesn't work because the chess program
\r
9165 doesn't "have the same console" as WinBoard. Maybe
\r
9166 we could arrange for this even though neither WinBoard
\r
9167 nor the chess program uses a console for stdio */
\r
9168 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9173 /* Can't interrupt */
\r
9177 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9184 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9186 char cmdLine[MSG_SIZ];
\r
9188 if (port[0] == NULLCHAR) {
\r
9189 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9191 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9193 return StartChildProcess(cmdLine, "", pr);
\r
9197 /* Code to open TCP sockets */
\r
9200 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9205 struct sockaddr_in sa, mysa;
\r
9206 struct hostent FAR *hp;
\r
9207 unsigned short uport;
\r
9208 WORD wVersionRequested;
\r
9211 /* Initialize socket DLL */
\r
9212 wVersionRequested = MAKEWORD(1, 1);
\r
9213 err = WSAStartup(wVersionRequested, &wsaData);
\r
9214 if (err != 0) return err;
\r
9217 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9218 err = WSAGetLastError();
\r
9223 /* Bind local address using (mostly) don't-care values.
\r
9225 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9226 mysa.sin_family = AF_INET;
\r
9227 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9228 uport = (unsigned short) 0;
\r
9229 mysa.sin_port = htons(uport);
\r
9230 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9231 == SOCKET_ERROR) {
\r
9232 err = WSAGetLastError();
\r
9237 /* Resolve remote host name */
\r
9238 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9239 if (!(hp = gethostbyname(host))) {
\r
9240 unsigned int b0, b1, b2, b3;
\r
9242 err = WSAGetLastError();
\r
9244 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9245 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9246 hp->h_addrtype = AF_INET;
\r
9248 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9249 hp->h_addr_list[0] = (char *) malloc(4);
\r
9250 hp->h_addr_list[0][0] = (char) b0;
\r
9251 hp->h_addr_list[0][1] = (char) b1;
\r
9252 hp->h_addr_list[0][2] = (char) b2;
\r
9253 hp->h_addr_list[0][3] = (char) b3;
\r
9259 sa.sin_family = hp->h_addrtype;
\r
9260 uport = (unsigned short) atoi(port);
\r
9261 sa.sin_port = htons(uport);
\r
9262 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9264 /* Make connection */
\r
9265 if (connect(s, (struct sockaddr *) &sa,
\r
9266 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9267 err = WSAGetLastError();
\r
9272 /* Prepare return value */
\r
9273 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9274 cp->kind = CPSock;
\r
9276 *pr = (ProcRef *) cp;
\r
9282 OpenCommPort(char *name, ProcRef *pr)
\r
9287 char fullname[MSG_SIZ];
\r
9289 if (*name != '\\')
\r
9290 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9292 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9294 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9295 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9296 if (h == (HANDLE) -1) {
\r
9297 return GetLastError();
\r
9301 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9303 /* Accumulate characters until a 100ms pause, then parse */
\r
9304 ct.ReadIntervalTimeout = 100;
\r
9305 ct.ReadTotalTimeoutMultiplier = 0;
\r
9306 ct.ReadTotalTimeoutConstant = 0;
\r
9307 ct.WriteTotalTimeoutMultiplier = 0;
\r
9308 ct.WriteTotalTimeoutConstant = 0;
\r
9309 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9311 /* Prepare return value */
\r
9312 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9313 cp->kind = CPComm;
\r
9316 *pr = (ProcRef *) cp;
\r
9322 OpenLoopback(ProcRef *pr)
\r
9324 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9330 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9335 struct sockaddr_in sa, mysa;
\r
9336 struct hostent FAR *hp;
\r
9337 unsigned short uport;
\r
9338 WORD wVersionRequested;
\r
9341 char stderrPortStr[MSG_SIZ];
\r
9343 /* Initialize socket DLL */
\r
9344 wVersionRequested = MAKEWORD(1, 1);
\r
9345 err = WSAStartup(wVersionRequested, &wsaData);
\r
9346 if (err != 0) return err;
\r
9348 /* Resolve remote host name */
\r
9349 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9350 if (!(hp = gethostbyname(host))) {
\r
9351 unsigned int b0, b1, b2, b3;
\r
9353 err = WSAGetLastError();
\r
9355 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9356 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9357 hp->h_addrtype = AF_INET;
\r
9359 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9360 hp->h_addr_list[0] = (char *) malloc(4);
\r
9361 hp->h_addr_list[0][0] = (char) b0;
\r
9362 hp->h_addr_list[0][1] = (char) b1;
\r
9363 hp->h_addr_list[0][2] = (char) b2;
\r
9364 hp->h_addr_list[0][3] = (char) b3;
\r
9370 sa.sin_family = hp->h_addrtype;
\r
9371 uport = (unsigned short) 514;
\r
9372 sa.sin_port = htons(uport);
\r
9373 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9375 /* Bind local socket to unused "privileged" port address
\r
9377 s = INVALID_SOCKET;
\r
9378 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9379 mysa.sin_family = AF_INET;
\r
9380 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9381 for (fromPort = 1023;; fromPort--) {
\r
9382 if (fromPort < 0) {
\r
9384 return WSAEADDRINUSE;
\r
9386 if (s == INVALID_SOCKET) {
\r
9387 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9388 err = WSAGetLastError();
\r
9393 uport = (unsigned short) fromPort;
\r
9394 mysa.sin_port = htons(uport);
\r
9395 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9396 == SOCKET_ERROR) {
\r
9397 err = WSAGetLastError();
\r
9398 if (err == WSAEADDRINUSE) continue;
\r
9402 if (connect(s, (struct sockaddr *) &sa,
\r
9403 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9404 err = WSAGetLastError();
\r
9405 if (err == WSAEADDRINUSE) {
\r
9416 /* Bind stderr local socket to unused "privileged" port address
\r
9418 s2 = INVALID_SOCKET;
\r
9419 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9420 mysa.sin_family = AF_INET;
\r
9421 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9422 for (fromPort = 1023;; fromPort--) {
\r
9423 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9424 if (fromPort < 0) {
\r
9425 (void) closesocket(s);
\r
9427 return WSAEADDRINUSE;
\r
9429 if (s2 == INVALID_SOCKET) {
\r
9430 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9431 err = WSAGetLastError();
\r
9437 uport = (unsigned short) fromPort;
\r
9438 mysa.sin_port = htons(uport);
\r
9439 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9440 == SOCKET_ERROR) {
\r
9441 err = WSAGetLastError();
\r
9442 if (err == WSAEADDRINUSE) continue;
\r
9443 (void) closesocket(s);
\r
9447 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9448 err = WSAGetLastError();
\r
9449 if (err == WSAEADDRINUSE) {
\r
9451 s2 = INVALID_SOCKET;
\r
9454 (void) closesocket(s);
\r
9455 (void) closesocket(s2);
\r
9461 prevStderrPort = fromPort; // remember port used
\r
9462 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9464 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9465 err = WSAGetLastError();
\r
9466 (void) closesocket(s);
\r
9467 (void) closesocket(s2);
\r
9472 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9473 err = WSAGetLastError();
\r
9474 (void) closesocket(s);
\r
9475 (void) closesocket(s2);
\r
9479 if (*user == NULLCHAR) user = UserName();
\r
9480 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9481 err = WSAGetLastError();
\r
9482 (void) closesocket(s);
\r
9483 (void) closesocket(s2);
\r
9487 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9488 err = WSAGetLastError();
\r
9489 (void) closesocket(s);
\r
9490 (void) closesocket(s2);
\r
9495 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9496 err = WSAGetLastError();
\r
9497 (void) closesocket(s);
\r
9498 (void) closesocket(s2);
\r
9502 (void) closesocket(s2); /* Stop listening */
\r
9504 /* Prepare return value */
\r
9505 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9506 cp->kind = CPRcmd;
\r
9509 *pr = (ProcRef *) cp;
\r
9516 AddInputSource(ProcRef pr, int lineByLine,
\r
9517 InputCallback func, VOIDSTAR closure)
\r
9519 InputSource *is, *is2 = NULL;
\r
9520 ChildProc *cp = (ChildProc *) pr;
\r
9522 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9523 is->lineByLine = lineByLine;
\r
9525 is->closure = closure;
\r
9526 is->second = NULL;
\r
9527 is->next = is->buf;
\r
9528 if (pr == NoProc) {
\r
9529 is->kind = CPReal;
\r
9530 consoleInputSource = is;
\r
9532 is->kind = cp->kind;
\r
9534 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9535 we create all threads suspended so that the is->hThread variable can be
\r
9536 safely assigned, then let the threads start with ResumeThread.
\r
9538 switch (cp->kind) {
\r
9540 is->hFile = cp->hFrom;
\r
9541 cp->hFrom = NULL; /* now owned by InputThread */
\r
9543 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9544 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9548 is->hFile = cp->hFrom;
\r
9549 cp->hFrom = NULL; /* now owned by InputThread */
\r
9551 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9552 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9556 is->sock = cp->sock;
\r
9558 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9559 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9563 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9565 is->sock = cp->sock;
\r
9567 is2->sock = cp->sock2;
\r
9568 is2->second = is2;
\r
9570 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9571 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9573 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9574 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9578 if( is->hThread != NULL ) {
\r
9579 ResumeThread( is->hThread );
\r
9582 if( is2 != NULL && is2->hThread != NULL ) {
\r
9583 ResumeThread( is2->hThread );
\r
9587 return (InputSourceRef) is;
\r
9591 RemoveInputSource(InputSourceRef isr)
\r
9595 is = (InputSource *) isr;
\r
9596 is->hThread = NULL; /* tell thread to stop */
\r
9597 CloseHandle(is->hThread);
\r
9598 if (is->second != NULL) {
\r
9599 is->second->hThread = NULL;
\r
9600 CloseHandle(is->second->hThread);
\r
9604 int no_wrap(char *message, int count)
\r
9606 ConsoleOutput(message, count, FALSE);
\r
9611 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9614 int outCount = SOCKET_ERROR;
\r
9615 ChildProc *cp = (ChildProc *) pr;
\r
9616 static OVERLAPPED ovl;
\r
9617 static int line = 0;
\r
9621 if (appData.noJoin || !appData.useInternalWrap)
\r
9622 return no_wrap(message, count);
\r
9625 int width = get_term_width();
\r
9626 int len = wrap(NULL, message, count, width, &line);
\r
9627 char *msg = malloc(len);
\r
9631 return no_wrap(message, count);
\r
9634 dbgchk = wrap(msg, message, count, width, &line);
\r
9635 if (dbgchk != len && appData.debugMode)
\r
9636 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9637 ConsoleOutput(msg, len, FALSE);
\r
9644 if (ovl.hEvent == NULL) {
\r
9645 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9647 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9649 switch (cp->kind) {
\r
9652 outCount = send(cp->sock, message, count, 0);
\r
9653 if (outCount == SOCKET_ERROR) {
\r
9654 *outError = WSAGetLastError();
\r
9656 *outError = NO_ERROR;
\r
9661 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9662 &dOutCount, NULL)) {
\r
9663 *outError = NO_ERROR;
\r
9664 outCount = (int) dOutCount;
\r
9666 *outError = GetLastError();
\r
9671 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9672 &dOutCount, &ovl);
\r
9673 if (*outError == NO_ERROR) {
\r
9674 outCount = (int) dOutCount;
\r
9684 if(n != 0) Sleep(n);
\r
9688 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9691 /* Ignore delay, not implemented for WinBoard */
\r
9692 return OutputToProcess(pr, message, count, outError);
\r
9697 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9698 char *buf, int count, int error)
\r
9700 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9703 /* see wgamelist.c for Game List functions */
\r
9704 /* see wedittags.c for Edit Tags functions */
\r
9711 char buf[MSG_SIZ];
\r
9714 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9715 f = fopen(buf, "r");
\r
9717 ProcessICSInitScript(f);
\r
9725 StartAnalysisClock()
\r
9727 if (analysisTimerEvent) return;
\r
9728 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9729 (UINT) 2000, NULL);
\r
9733 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9735 highlightInfo.sq[0].x = fromX;
\r
9736 highlightInfo.sq[0].y = fromY;
\r
9737 highlightInfo.sq[1].x = toX;
\r
9738 highlightInfo.sq[1].y = toY;
\r
9744 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9745 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9749 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9751 premoveHighlightInfo.sq[0].x = fromX;
\r
9752 premoveHighlightInfo.sq[0].y = fromY;
\r
9753 premoveHighlightInfo.sq[1].x = toX;
\r
9754 premoveHighlightInfo.sq[1].y = toY;
\r
9758 ClearPremoveHighlights()
\r
9760 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9761 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9765 ShutDownFrontEnd()
\r
9767 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9768 DeleteClipboardTempFiles();
\r
9774 if (IsIconic(hwndMain))
\r
9775 ShowWindow(hwndMain, SW_RESTORE);
\r
9777 SetActiveWindow(hwndMain);
\r
9781 * Prototypes for animation support routines
\r
9783 static void ScreenSquare(int column, int row, POINT * pt);
\r
9784 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9785 POINT frames[], int * nFrames);
\r
9791 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9792 { // [HGM] atomic: animate blast wave
\r
9795 explodeInfo.fromX = fromX;
\r
9796 explodeInfo.fromY = fromY;
\r
9797 explodeInfo.toX = toX;
\r
9798 explodeInfo.toY = toY;
\r
9799 for(i=1; i<4*kFactor; i++) {
\r
9800 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9801 DrawPosition(FALSE, board);
\r
9802 Sleep(appData.animSpeed);
\r
9804 explodeInfo.radius = 0;
\r
9805 DrawPosition(TRUE, board);
\r
9809 AnimateMove(board, fromX, fromY, toX, toY)
\r
9816 ChessSquare piece;
\r
9817 POINT start, finish, mid;
\r
9818 POINT frames[kFactor * 2 + 1];
\r
9821 if (!appData.animate) return;
\r
9822 if (doingSizing) return;
\r
9823 if (fromY < 0 || fromX < 0) return;
\r
9824 piece = board[fromY][fromX];
\r
9825 if (piece >= EmptySquare) return;
\r
9827 ScreenSquare(fromX, fromY, &start);
\r
9828 ScreenSquare(toX, toY, &finish);
\r
9830 /* All moves except knight jumps move in straight line */
\r
9831 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9832 mid.x = start.x + (finish.x - start.x) / 2;
\r
9833 mid.y = start.y + (finish.y - start.y) / 2;
\r
9835 /* Knight: make straight movement then diagonal */
\r
9836 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9837 mid.x = start.x + (finish.x - start.x) / 2;
\r
9841 mid.y = start.y + (finish.y - start.y) / 2;
\r
9845 /* Don't use as many frames for very short moves */
\r
9846 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9847 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9849 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9851 animInfo.from.x = fromX;
\r
9852 animInfo.from.y = fromY;
\r
9853 animInfo.to.x = toX;
\r
9854 animInfo.to.y = toY;
\r
9855 animInfo.lastpos = start;
\r
9856 animInfo.piece = piece;
\r
9857 for (n = 0; n < nFrames; n++) {
\r
9858 animInfo.pos = frames[n];
\r
9859 DrawPosition(FALSE, NULL);
\r
9860 animInfo.lastpos = animInfo.pos;
\r
9861 Sleep(appData.animSpeed);
\r
9863 animInfo.pos = finish;
\r
9864 DrawPosition(FALSE, NULL);
\r
9865 animInfo.piece = EmptySquare;
\r
9866 Explode(board, fromX, fromY, toX, toY);
\r
9869 /* Convert board position to corner of screen rect and color */
\r
9872 ScreenSquare(column, row, pt)
\r
9873 int column; int row; POINT * pt;
\r
9876 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9877 pt->y = lineGap + row * (squareSize + lineGap);
\r
9879 pt->x = lineGap + column * (squareSize + lineGap);
\r
9880 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9884 /* Generate a series of frame coords from start->mid->finish.
\r
9885 The movement rate doubles until the half way point is
\r
9886 reached, then halves back down to the final destination,
\r
9887 which gives a nice slow in/out effect. The algorithmn
\r
9888 may seem to generate too many intermediates for short
\r
9889 moves, but remember that the purpose is to attract the
\r
9890 viewers attention to the piece about to be moved and
\r
9891 then to where it ends up. Too few frames would be less
\r
9895 Tween(start, mid, finish, factor, frames, nFrames)
\r
9896 POINT * start; POINT * mid;
\r
9897 POINT * finish; int factor;
\r
9898 POINT frames[]; int * nFrames;
\r
9900 int n, fraction = 1, count = 0;
\r
9902 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9903 for (n = 0; n < factor; n++)
\r
9905 for (n = 0; n < factor; n++) {
\r
9906 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9907 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9909 fraction = fraction / 2;
\r
9913 frames[count] = *mid;
\r
9916 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9918 for (n = 0; n < factor; n++) {
\r
9919 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9920 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9922 fraction = fraction * 2;
\r
9928 SettingsPopUp(ChessProgramState *cps)
\r
9929 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9930 EngineOptionsPopup(savedHwnd, cps);
\r
9933 int flock(int fid, int code)
\r
9935 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9939 ov.OffsetHigh = 0;
\r
9941 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9942 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9943 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9944 default: return -1;
\r