2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][42] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
257 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
259 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
262 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
265 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
266 { ABOUTBOX2, IDC_ChessBoard },
\r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
268 OPT_GameListClose, IDC_GameListDoFilter },
\r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
270 { DLG_Error, IDOK },
\r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
272 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
275 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
276 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
277 { DLG_IndexNumber, IDC_Index },
\r
278 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
279 { DLG_TypeInName, IDOK, IDCANCEL },
\r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
281 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
283 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
284 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
285 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
286 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
287 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
288 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
290 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
291 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
292 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
293 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
294 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
295 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
296 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
297 GPB_General, GPB_Alarm },
\r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
299 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
300 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
301 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
302 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
303 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
304 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
305 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
307 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
308 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
309 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
310 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
311 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
312 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
313 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
314 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
316 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
317 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
318 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
319 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
322 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
323 { DLG_MoveHistory },
\r
324 { DLG_EvalGraph },
\r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
328 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
329 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
330 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
332 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
333 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
337 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
338 static int lastChecked;
\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
340 extern int tinyLayout;
\r
341 extern char * menuBarText[][10];
\r
344 LoadLanguageFile(char *name)
\r
345 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
347 int i=0, j=0, n=0, k;
\r
350 if(!name || name[0] == NULLCHAR) return;
\r
351 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
352 appData.language = oldLanguage;
\r
353 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
354 if((f = fopen(buf, "r")) == NULL) return;
\r
355 while((k = fgetc(f)) != EOF) {
\r
356 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
357 languageBuf[i] = k;
\r
359 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
361 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
362 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
363 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
364 english[j] = languageBuf + n + 1; *p = 0;
\r
365 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
371 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
373 case 'n': k = '\n'; break;
\r
374 case 'r': k = '\r'; break;
\r
375 case 't': k = '\t'; break;
\r
377 languageBuf[--i] = k;
\r
382 barbaric = (j != 0);
\r
383 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
388 { // return the translation of the given string
\r
389 // efficiency can be improved a lot...
\r
391 static char buf[MSG_SIZ];
\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
393 if(!barbaric) return s;
\r
394 if(!s) return ""; // sanity
\r
395 while(english[i]) {
\r
396 if(!strcmp(s, english[i])) return foreign[i];
\r
397 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
398 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
407 Translate(HWND hDlg, int dialogID)
\r
408 { // translate all text items in the given dialog
\r
410 char buf[MSG_SIZ], *s;
\r
411 if(!barbaric) return;
\r
412 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
413 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
414 GetWindowText( hDlg, buf, MSG_SIZ );
\r
416 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
417 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
418 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
419 if(strlen(buf) == 0) continue;
\r
421 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
426 TranslateOneMenu(int i, HMENU subMenu)
\r
429 static MENUITEMINFO info;
\r
431 info.cbSize = sizeof(MENUITEMINFO);
\r
432 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
433 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
435 info.dwTypeData = buf;
\r
436 info.cch = sizeof(buf);
\r
437 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
439 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
440 else menuText[i][j] = strdup(buf); // remember original on first change
\r
442 if(buf[0] == NULLCHAR) continue;
\r
443 info.dwTypeData = T_(buf);
\r
444 info.cch = strlen(buf)+1;
\r
445 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
451 TranslateMenus(int addLanguage)
\r
454 WIN32_FIND_DATA fileData;
\r
456 #define IDM_English 1970
\r
458 HMENU mainMenu = GetMenu(hwndMain);
\r
459 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
460 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
461 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
462 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
463 TranslateOneMenu(i, subMenu);
\r
465 DrawMenuBar(hwndMain);
\r
468 if(!addLanguage) return;
\r
469 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
470 HMENU mainMenu = GetMenu(hwndMain);
\r
471 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
472 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
473 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
474 i = 0; lastChecked = IDM_English;
\r
476 char *p, *q = fileData.cFileName;
\r
477 int checkFlag = MF_UNCHECKED;
\r
478 languageFile[i] = strdup(q);
\r
479 if(barbaric && !strcmp(oldLanguage, q)) {
\r
480 checkFlag = MF_CHECKED;
\r
481 lastChecked = IDM_English + i + 1;
\r
482 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
484 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
485 p = strstr(fileData.cFileName, ".lng");
\r
487 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
488 } while(FindNextFile(hFind, &fileData));
\r
495 #define IDM_RecentEngines 3000
\r
498 RecentEngineMenu (char *s)
\r
500 if(appData.icsActive) return;
\r
501 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
502 HMENU mainMenu = GetMenu(hwndMain);
\r
503 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
504 int i=IDM_RecentEngines;
\r
505 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
506 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
508 char *p = strchr(s, '\n');
\r
509 if(p == NULL) return; // malformed!
\r
511 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
525 int cliWidth, cliHeight;
\r
528 SizeInfo sizeInfo[] =
\r
530 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
531 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
532 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
533 { "petite", 33, 1, 1, 1, 0, 0 },
\r
534 { "slim", 37, 2, 1, 0, 0, 0 },
\r
535 { "small", 40, 2, 1, 0, 0, 0 },
\r
536 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
537 { "middling", 49, 2, 0, 0, 0, 0 },
\r
538 { "average", 54, 2, 0, 0, 0, 0 },
\r
539 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
540 { "medium", 64, 3, 0, 0, 0, 0 },
\r
541 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
542 { "large", 80, 3, 0, 0, 0, 0 },
\r
543 { "big", 87, 3, 0, 0, 0, 0 },
\r
544 { "huge", 95, 3, 0, 0, 0, 0 },
\r
545 { "giant", 108, 3, 0, 0, 0, 0 },
\r
546 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
547 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
548 { NULL, 0, 0, 0, 0, 0, 0 }
\r
551 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
552 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
554 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
555 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
556 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
557 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
558 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
559 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
560 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
561 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
562 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
563 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
564 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
565 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
566 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
567 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
568 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
569 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
570 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
571 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
574 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
583 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
584 #define N_BUTTONS 5
\r
586 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
588 {"<<", IDM_ToStart, NULL, NULL},
\r
589 {"<", IDM_Backward, NULL, NULL},
\r
590 {"P", IDM_Pause, NULL, NULL},
\r
591 {">", IDM_Forward, NULL, NULL},
\r
592 {">>", IDM_ToEnd, NULL, NULL},
\r
595 int tinyLayout = 0, smallLayout = 0;
\r
596 #define MENU_BAR_ITEMS 9
\r
597 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
598 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
599 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
603 MySound sounds[(int)NSoundClasses];
\r
604 MyTextAttribs textAttribs[(int)NColorClasses];
\r
606 MyColorizeAttribs colorizeAttribs[] = {
\r
607 { (COLORREF)0, 0, N_("Shout Text") },
\r
608 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
609 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
610 { (COLORREF)0, 0, N_("Channel Text") },
\r
611 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
612 { (COLORREF)0, 0, N_("Tell Text") },
\r
613 { (COLORREF)0, 0, N_("Challenge Text") },
\r
614 { (COLORREF)0, 0, N_("Request Text") },
\r
615 { (COLORREF)0, 0, N_("Seek Text") },
\r
616 { (COLORREF)0, 0, N_("Normal Text") },
\r
617 { (COLORREF)0, 0, N_("None") }
\r
622 static char *commentTitle;
\r
623 static char *commentText;
\r
624 static int commentIndex;
\r
625 static Boolean editComment = FALSE;
\r
628 char errorTitle[MSG_SIZ];
\r
629 char errorMessage[2*MSG_SIZ];
\r
630 HWND errorDialog = NULL;
\r
631 BOOLEAN moveErrorMessageUp = FALSE;
\r
632 BOOLEAN consoleEcho = TRUE;
\r
633 CHARFORMAT consoleCF;
\r
634 COLORREF consoleBackgroundColor;
\r
636 char *programVersion;
\r
642 typedef int CPKind;
\r
651 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
654 #define INPUT_SOURCE_BUF_SIZE 4096
\r
656 typedef struct _InputSource {
\r
663 char buf[INPUT_SOURCE_BUF_SIZE];
\r
667 InputCallback func;
\r
668 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
672 InputSource *consoleInputSource;
\r
677 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
678 VOID ConsoleCreate();
\r
680 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
681 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
682 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
683 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
685 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
686 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
687 void ParseIcsTextMenu(char *icsTextMenuString);
\r
688 VOID PopUpNameDialog(char firstchar);
\r
689 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
693 int GameListOptions();
\r
695 int dummy; // [HGM] for obsolete args
\r
697 HWND hwndMain = NULL; /* root window*/
\r
698 HWND hwndConsole = NULL;
\r
699 HWND commentDialog = NULL;
\r
700 HWND moveHistoryDialog = NULL;
\r
701 HWND evalGraphDialog = NULL;
\r
702 HWND engineOutputDialog = NULL;
\r
703 HWND gameListDialog = NULL;
\r
704 HWND editTagsDialog = NULL;
\r
706 int commentUp = FALSE;
\r
708 WindowPlacement wpMain;
\r
709 WindowPlacement wpConsole;
\r
710 WindowPlacement wpComment;
\r
711 WindowPlacement wpMoveHistory;
\r
712 WindowPlacement wpEvalGraph;
\r
713 WindowPlacement wpEngineOutput;
\r
714 WindowPlacement wpGameList;
\r
715 WindowPlacement wpTags;
\r
717 VOID EngineOptionsPopup(); // [HGM] settings
\r
719 VOID GothicPopUp(char *title, VariantClass variant);
\r
721 * Setting "frozen" should disable all user input other than deleting
\r
722 * the window. We do this while engines are initializing themselves.
\r
724 static int frozen = 0;
\r
725 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
731 if (frozen) return;
\r
733 hmenu = GetMenu(hwndMain);
\r
734 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
735 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
737 DrawMenuBar(hwndMain);
\r
740 /* Undo a FreezeUI */
\r
746 if (!frozen) return;
\r
748 hmenu = GetMenu(hwndMain);
\r
749 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
750 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
752 DrawMenuBar(hwndMain);
\r
755 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
757 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
763 #define JAWS_ALT_INTERCEPT
\r
764 #define JAWS_KBUP_NAVIGATION
\r
765 #define JAWS_KBDOWN_NAVIGATION
\r
766 #define JAWS_MENU_ITEMS
\r
767 #define JAWS_SILENCE
\r
768 #define JAWS_REPLAY
\r
770 #define JAWS_COPYRIGHT
\r
771 #define JAWS_DELETE(X) X
\r
772 #define SAYMACHINEMOVE()
\r
776 /*---------------------------------------------------------------------------*\
\r
780 \*---------------------------------------------------------------------------*/
\r
783 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
784 LPSTR lpCmdLine, int nCmdShow)
\r
787 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
788 // INITCOMMONCONTROLSEX ex;
\r
792 LoadLibrary("RICHED32.DLL");
\r
793 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
795 if (!InitApplication(hInstance)) {
\r
798 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
805 // InitCommonControlsEx(&ex);
\r
806 InitCommonControls();
\r
808 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
809 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
810 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
812 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
814 while (GetMessage(&msg, /* message structure */
\r
815 NULL, /* handle of window receiving the message */
\r
816 0, /* lowest message to examine */
\r
817 0)) /* highest message to examine */
\r
820 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
821 // [HGM] navigate: switch between all windows with tab
\r
822 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
823 int i, currentElement = 0;
\r
825 // first determine what element of the chain we come from (if any)
\r
826 if(appData.icsActive) {
\r
827 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
828 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
830 if(engineOutputDialog && EngineOutputIsUp()) {
\r
831 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
832 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
834 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
835 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
837 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
838 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
839 if(msg.hwnd == e1) currentElement = 2; else
\r
840 if(msg.hwnd == e2) currentElement = 3; else
\r
841 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
842 if(msg.hwnd == mh) currentElement = 4; else
\r
843 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
844 if(msg.hwnd == hText) currentElement = 5; else
\r
845 if(msg.hwnd == hInput) currentElement = 6; else
\r
846 for (i = 0; i < N_BUTTONS; i++) {
\r
847 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
850 // determine where to go to
\r
851 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
853 currentElement = (currentElement + direction) % 7;
\r
854 switch(currentElement) {
\r
856 h = hwndMain; break; // passing this case always makes the loop exit
\r
858 h = buttonDesc[0].hwnd; break; // could be NULL
\r
860 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
863 if(!EngineOutputIsUp()) continue;
\r
866 if(!MoveHistoryIsUp()) continue;
\r
868 // case 6: // input to eval graph does not seem to get here!
\r
869 // if(!EvalGraphIsUp()) continue;
\r
870 // h = evalGraphDialog; break;
\r
872 if(!appData.icsActive) continue;
\r
876 if(!appData.icsActive) continue;
\r
882 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
883 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
886 continue; // this message now has been processed
\r
890 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
891 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
892 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
893 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
894 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
895 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
896 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
897 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
898 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
899 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
900 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
901 for(i=0; i<MAX_CHAT; i++)
\r
902 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
905 if(done) continue; // [HGM] chat: end patch
\r
906 TranslateMessage(&msg); /* Translates virtual key codes */
\r
907 DispatchMessage(&msg); /* Dispatches message to window */
\r
912 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
915 /*---------------------------------------------------------------------------*\
\r
917 * Initialization functions
\r
919 \*---------------------------------------------------------------------------*/
\r
923 { // update user logo if necessary
\r
924 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
926 if(appData.autoLogo) {
\r
927 curName = UserName();
\r
928 if(strcmp(curName, oldUserName)) {
\r
929 GetCurrentDirectory(MSG_SIZ, dir);
\r
930 SetCurrentDirectory(installDir);
\r
931 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
932 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
933 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
934 if(userLogo == NULL)
\r
935 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
936 SetCurrentDirectory(dir); /* return to prev directory */
\r
942 InitApplication(HINSTANCE hInstance)
\r
946 /* Fill in window class structure with parameters that describe the */
\r
949 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
950 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
951 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
952 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
953 wc.hInstance = hInstance; /* Owner of this class */
\r
954 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
955 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
956 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
957 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
958 wc.lpszClassName = szAppName; /* Name to register as */
\r
960 /* Register the window class and return success/failure code. */
\r
961 if (!RegisterClass(&wc)) return FALSE;
\r
963 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
964 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
966 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
967 wc.hInstance = hInstance;
\r
968 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
969 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
970 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
971 wc.lpszMenuName = NULL;
\r
972 wc.lpszClassName = szConsoleName;
\r
974 if (!RegisterClass(&wc)) return FALSE;
\r
979 /* Set by InitInstance, used by EnsureOnScreen */
\r
980 int screenHeight, screenWidth;
\r
983 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
985 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
986 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
987 if (*x > screenWidth - 32) *x = 0;
\r
988 if (*y > screenHeight - 32) *y = 0;
\r
989 if (*x < minX) *x = minX;
\r
990 if (*y < minY) *y = minY;
\r
994 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
996 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
997 GetCurrentDirectory(MSG_SIZ, dir);
\r
998 SetCurrentDirectory(installDir);
\r
999 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1000 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1002 if (cps->programLogo == NULL && appData.debugMode) {
\r
1003 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1005 } else if(appData.autoLogo) {
\r
1006 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1007 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1008 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1010 if(appData.directory[n] && appData.directory[n][0]) {
\r
1011 SetCurrentDirectory(appData.directory[n]);
\r
1012 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1015 SetCurrentDirectory(dir); /* return to prev directory */
\r
1021 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1022 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1024 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1025 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1026 liteBackTextureMode = appData.liteBackTextureMode;
\r
1028 if (liteBackTexture == NULL && appData.debugMode) {
\r
1029 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1033 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1034 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1035 darkBackTextureMode = appData.darkBackTextureMode;
\r
1037 if (darkBackTexture == NULL && appData.debugMode) {
\r
1038 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1044 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1046 HWND hwnd; /* Main window handle. */
\r
1048 WINDOWPLACEMENT wp;
\r
1051 hInst = hInstance; /* Store instance handle in our global variable */
\r
1052 programName = szAppName;
\r
1054 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1055 *filepart = NULLCHAR;
\r
1057 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1059 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1060 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1061 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1062 /* xboard, and older WinBoards, controlled the move sound with the
\r
1063 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1064 always turn the option on (so that the backend will call us),
\r
1065 then let the user turn the sound off by setting it to silence if
\r
1066 desired. To accommodate old winboard.ini files saved by old
\r
1067 versions of WinBoard, we also turn off the sound if the option
\r
1068 was initially set to false. [HGM] taken out of InitAppData */
\r
1069 if (!appData.ringBellAfterMoves) {
\r
1070 sounds[(int)SoundMove].name = strdup("");
\r
1071 appData.ringBellAfterMoves = TRUE;
\r
1073 if (appData.debugMode) {
\r
1074 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1075 setbuf(debugFP, NULL);
\r
1078 LoadLanguageFile(appData.language);
\r
1082 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1083 // InitEngineUCI( installDir, &second );
\r
1085 /* Create a main window for this application instance. */
\r
1086 hwnd = CreateWindow(szAppName, szTitle,
\r
1087 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1088 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1089 NULL, NULL, hInstance, NULL);
\r
1092 /* If window could not be created, return "failure" */
\r
1097 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1098 LoadLogo(&first, 0, FALSE);
\r
1099 LoadLogo(&second, 1, appData.icsActive);
\r
1103 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1104 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1105 iconCurrent = iconWhite;
\r
1106 InitDrawingColors();
\r
1107 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1108 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1109 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1110 /* Compute window size for each board size, and use the largest
\r
1111 size that fits on this screen as the default. */
\r
1112 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1113 if (boardSize == (BoardSize)-1 &&
\r
1114 winH <= screenHeight
\r
1115 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1116 && winW <= screenWidth) {
\r
1117 boardSize = (BoardSize)ibs;
\r
1121 InitDrawingSizes(boardSize, 0);
\r
1122 RecentEngineMenu(appData.recentEngineList);
\r
1124 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1126 /* [AS] Load textures if specified */
\r
1129 mysrandom( (unsigned) time(NULL) );
\r
1131 /* [AS] Restore layout */
\r
1132 if( wpMoveHistory.visible ) {
\r
1133 MoveHistoryPopUp();
\r
1136 if( wpEvalGraph.visible ) {
\r
1140 if( wpEngineOutput.visible ) {
\r
1141 EngineOutputPopUp();
\r
1144 /* Make the window visible; update its client area; and return "success" */
\r
1145 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1146 wp.length = sizeof(WINDOWPLACEMENT);
\r
1148 wp.showCmd = nCmdShow;
\r
1149 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1150 wp.rcNormalPosition.left = wpMain.x;
\r
1151 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1152 wp.rcNormalPosition.top = wpMain.y;
\r
1153 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1154 SetWindowPlacement(hwndMain, &wp);
\r
1156 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1158 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1159 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1161 if (hwndConsole) {
\r
1163 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1164 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1166 ShowWindow(hwndConsole, nCmdShow);
\r
1167 SetActiveWindow(hwndConsole);
\r
1169 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1170 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1179 HMENU hmenu = GetMenu(hwndMain);
\r
1181 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1182 MF_BYCOMMAND|((appData.icsActive &&
\r
1183 *appData.icsCommPort != NULLCHAR) ?
\r
1184 MF_ENABLED : MF_GRAYED));
\r
1185 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1186 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1187 MF_CHECKED : MF_UNCHECKED));
\r
1190 //---------------------------------------------------------------------------------------------------------
\r
1192 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1193 #define XBOARD FALSE
\r
1195 #define OPTCHAR "/"
\r
1196 #define SEPCHAR "="
\r
1200 // front-end part of option handling
\r
1203 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1205 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1206 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1209 lf->lfEscapement = 0;
\r
1210 lf->lfOrientation = 0;
\r
1211 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1212 lf->lfItalic = mfp->italic;
\r
1213 lf->lfUnderline = mfp->underline;
\r
1214 lf->lfStrikeOut = mfp->strikeout;
\r
1215 lf->lfCharSet = mfp->charset;
\r
1216 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1217 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1218 lf->lfQuality = DEFAULT_QUALITY;
\r
1219 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1220 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1224 CreateFontInMF(MyFont *mf)
\r
1226 LFfromMFP(&mf->lf, &mf->mfp);
\r
1227 if (mf->hf) DeleteObject(mf->hf);
\r
1228 mf->hf = CreateFontIndirect(&mf->lf);
\r
1231 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1233 colorVariable[] = {
\r
1234 &whitePieceColor,
\r
1235 &blackPieceColor,
\r
1236 &lightSquareColor,
\r
1237 &darkSquareColor,
\r
1238 &highlightSquareColor,
\r
1239 &premoveHighlightColor,
\r
1241 &consoleBackgroundColor,
\r
1242 &appData.fontForeColorWhite,
\r
1243 &appData.fontBackColorWhite,
\r
1244 &appData.fontForeColorBlack,
\r
1245 &appData.fontBackColorBlack,
\r
1246 &appData.evalHistColorWhite,
\r
1247 &appData.evalHistColorBlack,
\r
1248 &appData.highlightArrowColor,
\r
1251 /* Command line font name parser. NULL name means do nothing.
\r
1252 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1253 For backward compatibility, syntax without the colon is also
\r
1254 accepted, but font names with digits in them won't work in that case.
\r
1257 ParseFontName(char *name, MyFontParams *mfp)
\r
1260 if (name == NULL) return;
\r
1262 q = strchr(p, ':');
\r
1264 if (q - p >= sizeof(mfp->faceName))
\r
1265 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1266 memcpy(mfp->faceName, p, q - p);
\r
1267 mfp->faceName[q - p] = NULLCHAR;
\r
1270 q = mfp->faceName;
\r
1271 while (*p && !isdigit(*p)) {
\r
1273 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1274 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1276 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1279 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1280 mfp->pointSize = (float) atof(p);
\r
1281 mfp->bold = (strchr(p, 'b') != NULL);
\r
1282 mfp->italic = (strchr(p, 'i') != NULL);
\r
1283 mfp->underline = (strchr(p, 'u') != NULL);
\r
1284 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1285 mfp->charset = DEFAULT_CHARSET;
\r
1286 q = strchr(p, 'c');
\r
1288 mfp->charset = (BYTE) atoi(q+1);
\r
1292 ParseFont(char *name, int number)
\r
1293 { // wrapper to shield back-end from 'font'
\r
1294 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1299 { // in WB we have a 2D array of fonts; this initializes their description
\r
1301 /* Point font array elements to structures and
\r
1302 parse default font names */
\r
1303 for (i=0; i<NUM_FONTS; i++) {
\r
1304 for (j=0; j<NUM_SIZES; j++) {
\r
1305 font[j][i] = &fontRec[j][i];
\r
1306 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1313 { // here we create the actual fonts from the selected descriptions
\r
1315 for (i=0; i<NUM_FONTS; i++) {
\r
1316 for (j=0; j<NUM_SIZES; j++) {
\r
1317 CreateFontInMF(font[j][i]);
\r
1321 /* Color name parser.
\r
1322 X version accepts X color names, but this one
\r
1323 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1325 ParseColorName(char *name)
\r
1327 int red, green, blue, count;
\r
1328 char buf[MSG_SIZ];
\r
1330 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1332 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1333 &red, &green, &blue);
\r
1336 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1337 DisplayError(buf, 0);
\r
1338 return RGB(0, 0, 0);
\r
1340 return PALETTERGB(red, green, blue);
\r
1344 ParseColor(int n, char *name)
\r
1345 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1346 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1350 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1352 char *e = argValue;
\r
1356 if (*e == 'b') eff |= CFE_BOLD;
\r
1357 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1358 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1359 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1360 else if (*e == '#' || isdigit(*e)) break;
\r
1364 *color = ParseColorName(e);
\r
1368 ParseTextAttribs(ColorClass cc, char *s)
\r
1369 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1370 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1371 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1375 ParseBoardSize(void *addr, char *name)
\r
1376 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1377 BoardSize bs = SizeTiny;
\r
1378 while (sizeInfo[bs].name != NULL) {
\r
1379 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1380 *(BoardSize *)addr = bs;
\r
1385 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1390 { // [HGM] import name from appData first
\r
1393 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1394 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1395 textAttribs[cc].sound.data = NULL;
\r
1396 MyLoadSound(&textAttribs[cc].sound);
\r
1398 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1399 textAttribs[cc].sound.name = strdup("");
\r
1400 textAttribs[cc].sound.data = NULL;
\r
1402 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1403 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1404 sounds[sc].data = NULL;
\r
1405 MyLoadSound(&sounds[sc]);
\r
1410 SetCommPortDefaults()
\r
1412 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1413 dcb.DCBlength = sizeof(DCB);
\r
1414 dcb.BaudRate = 9600;
\r
1415 dcb.fBinary = TRUE;
\r
1416 dcb.fParity = FALSE;
\r
1417 dcb.fOutxCtsFlow = FALSE;
\r
1418 dcb.fOutxDsrFlow = FALSE;
\r
1419 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1420 dcb.fDsrSensitivity = FALSE;
\r
1421 dcb.fTXContinueOnXoff = TRUE;
\r
1422 dcb.fOutX = FALSE;
\r
1424 dcb.fNull = FALSE;
\r
1425 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1426 dcb.fAbortOnError = FALSE;
\r
1428 dcb.Parity = SPACEPARITY;
\r
1429 dcb.StopBits = ONESTOPBIT;
\r
1432 // [HGM] args: these three cases taken out to stay in front-end
\r
1434 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1435 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1436 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1437 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1439 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1440 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1441 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1442 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1443 ad->argName, mfp->faceName, mfp->pointSize,
\r
1444 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1445 mfp->bold ? "b" : "",
\r
1446 mfp->italic ? "i" : "",
\r
1447 mfp->underline ? "u" : "",
\r
1448 mfp->strikeout ? "s" : "",
\r
1449 (int)mfp->charset);
\r
1455 { // [HGM] copy the names from the internal WB variables to appData
\r
1458 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1459 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1460 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1461 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1465 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1466 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1467 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1468 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1469 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1470 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1471 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1472 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1473 (ta->effects) ? " " : "",
\r
1474 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1478 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1479 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1480 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1481 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1482 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1486 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1487 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1488 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1492 ParseCommPortSettings(char *s)
\r
1493 { // wrapper to keep dcb from back-end
\r
1494 ParseCommSettings(s, &dcb);
\r
1499 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1500 GetActualPlacement(hwndMain, &wpMain);
\r
1501 GetActualPlacement(hwndConsole, &wpConsole);
\r
1502 GetActualPlacement(commentDialog, &wpComment);
\r
1503 GetActualPlacement(editTagsDialog, &wpTags);
\r
1504 GetActualPlacement(gameListDialog, &wpGameList);
\r
1505 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1506 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1507 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1511 PrintCommPortSettings(FILE *f, char *name)
\r
1512 { // wrapper to shield back-end from DCB
\r
1513 PrintCommSettings(f, name, &dcb);
\r
1517 MySearchPath(char *installDir, char *name, char *fullname)
\r
1519 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1520 if(name[0]== '%') {
\r
1521 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1522 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1523 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1524 *strchr(buf, '%') = 0;
\r
1525 strcat(fullname, getenv(buf));
\r
1526 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1528 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1529 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1530 return (int) strlen(fullname);
\r
1532 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1536 MyGetFullPathName(char *name, char *fullname)
\r
1539 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1544 { // [HGM] args: allows testing if main window is realized from back-end
\r
1545 return hwndMain != NULL;
\r
1549 PopUpStartupDialog()
\r
1553 LoadLanguageFile(appData.language);
\r
1554 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1555 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1556 FreeProcInstance(lpProc);
\r
1559 /*---------------------------------------------------------------------------*\
\r
1561 * GDI board drawing routines
\r
1563 \*---------------------------------------------------------------------------*/
\r
1565 /* [AS] Draw square using background texture */
\r
1566 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1571 return; /* Should never happen! */
\r
1574 SetGraphicsMode( dst, GM_ADVANCED );
\r
1581 /* X reflection */
\r
1586 x.eDx = (FLOAT) dw + dx - 1;
\r
1589 SetWorldTransform( dst, &x );
\r
1592 /* Y reflection */
\r
1598 x.eDy = (FLOAT) dh + dy - 1;
\r
1600 SetWorldTransform( dst, &x );
\r
1608 x.eDx = (FLOAT) dx;
\r
1609 x.eDy = (FLOAT) dy;
\r
1612 SetWorldTransform( dst, &x );
\r
1616 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1624 SetWorldTransform( dst, &x );
\r
1626 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1629 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1631 PM_WP = (int) WhitePawn,
\r
1632 PM_WN = (int) WhiteKnight,
\r
1633 PM_WB = (int) WhiteBishop,
\r
1634 PM_WR = (int) WhiteRook,
\r
1635 PM_WQ = (int) WhiteQueen,
\r
1636 PM_WF = (int) WhiteFerz,
\r
1637 PM_WW = (int) WhiteWazir,
\r
1638 PM_WE = (int) WhiteAlfil,
\r
1639 PM_WM = (int) WhiteMan,
\r
1640 PM_WO = (int) WhiteCannon,
\r
1641 PM_WU = (int) WhiteUnicorn,
\r
1642 PM_WH = (int) WhiteNightrider,
\r
1643 PM_WA = (int) WhiteAngel,
\r
1644 PM_WC = (int) WhiteMarshall,
\r
1645 PM_WAB = (int) WhiteCardinal,
\r
1646 PM_WD = (int) WhiteDragon,
\r
1647 PM_WL = (int) WhiteLance,
\r
1648 PM_WS = (int) WhiteCobra,
\r
1649 PM_WV = (int) WhiteFalcon,
\r
1650 PM_WSG = (int) WhiteSilver,
\r
1651 PM_WG = (int) WhiteGrasshopper,
\r
1652 PM_WK = (int) WhiteKing,
\r
1653 PM_BP = (int) BlackPawn,
\r
1654 PM_BN = (int) BlackKnight,
\r
1655 PM_BB = (int) BlackBishop,
\r
1656 PM_BR = (int) BlackRook,
\r
1657 PM_BQ = (int) BlackQueen,
\r
1658 PM_BF = (int) BlackFerz,
\r
1659 PM_BW = (int) BlackWazir,
\r
1660 PM_BE = (int) BlackAlfil,
\r
1661 PM_BM = (int) BlackMan,
\r
1662 PM_BO = (int) BlackCannon,
\r
1663 PM_BU = (int) BlackUnicorn,
\r
1664 PM_BH = (int) BlackNightrider,
\r
1665 PM_BA = (int) BlackAngel,
\r
1666 PM_BC = (int) BlackMarshall,
\r
1667 PM_BG = (int) BlackGrasshopper,
\r
1668 PM_BAB = (int) BlackCardinal,
\r
1669 PM_BD = (int) BlackDragon,
\r
1670 PM_BL = (int) BlackLance,
\r
1671 PM_BS = (int) BlackCobra,
\r
1672 PM_BV = (int) BlackFalcon,
\r
1673 PM_BSG = (int) BlackSilver,
\r
1674 PM_BK = (int) BlackKing
\r
1677 static HFONT hPieceFont = NULL;
\r
1678 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1679 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1680 static int fontBitmapSquareSize = 0;
\r
1681 static char pieceToFontChar[(int) EmptySquare] =
\r
1682 { 'p', 'n', 'b', 'r', 'q',
\r
1683 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1684 'k', 'o', 'm', 'v', 't', 'w',
\r
1685 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1688 extern BOOL SetCharTable( char *table, const char * map );
\r
1689 /* [HGM] moved to backend.c */
\r
1691 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1694 BYTE r1 = GetRValue( color );
\r
1695 BYTE g1 = GetGValue( color );
\r
1696 BYTE b1 = GetBValue( color );
\r
1702 /* Create a uniform background first */
\r
1703 hbrush = CreateSolidBrush( color );
\r
1704 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1705 FillRect( hdc, &rc, hbrush );
\r
1706 DeleteObject( hbrush );
\r
1709 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1710 int steps = squareSize / 2;
\r
1713 for( i=0; i<steps; i++ ) {
\r
1714 BYTE r = r1 - (r1-r2) * i / steps;
\r
1715 BYTE g = g1 - (g1-g2) * i / steps;
\r
1716 BYTE b = b1 - (b1-b2) * i / steps;
\r
1718 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1719 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1720 FillRect( hdc, &rc, hbrush );
\r
1721 DeleteObject(hbrush);
\r
1724 else if( mode == 2 ) {
\r
1725 /* Diagonal gradient, good more or less for every piece */
\r
1726 POINT triangle[3];
\r
1727 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1728 HBRUSH hbrush_old;
\r
1729 int steps = squareSize;
\r
1732 triangle[0].x = squareSize - steps;
\r
1733 triangle[0].y = squareSize;
\r
1734 triangle[1].x = squareSize;
\r
1735 triangle[1].y = squareSize;
\r
1736 triangle[2].x = squareSize;
\r
1737 triangle[2].y = squareSize - steps;
\r
1739 for( i=0; i<steps; i++ ) {
\r
1740 BYTE r = r1 - (r1-r2) * i / steps;
\r
1741 BYTE g = g1 - (g1-g2) * i / steps;
\r
1742 BYTE b = b1 - (b1-b2) * i / steps;
\r
1744 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1745 hbrush_old = SelectObject( hdc, hbrush );
\r
1746 Polygon( hdc, triangle, 3 );
\r
1747 SelectObject( hdc, hbrush_old );
\r
1748 DeleteObject(hbrush);
\r
1753 SelectObject( hdc, hpen );
\r
1758 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1759 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1760 piece: follow the steps as explained below.
\r
1762 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1766 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1770 int backColor = whitePieceColor;
\r
1771 int foreColor = blackPieceColor;
\r
1773 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1774 backColor = appData.fontBackColorWhite;
\r
1775 foreColor = appData.fontForeColorWhite;
\r
1777 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1778 backColor = appData.fontBackColorBlack;
\r
1779 foreColor = appData.fontForeColorBlack;
\r
1783 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1785 hbm_old = SelectObject( hdc, hbm );
\r
1789 rc.right = squareSize;
\r
1790 rc.bottom = squareSize;
\r
1792 /* Step 1: background is now black */
\r
1793 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1795 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1797 pt.x = (squareSize - sz.cx) / 2;
\r
1798 pt.y = (squareSize - sz.cy) / 2;
\r
1800 SetBkMode( hdc, TRANSPARENT );
\r
1801 SetTextColor( hdc, chroma );
\r
1802 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1803 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1805 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1806 /* Step 3: the area outside the piece is filled with white */
\r
1807 // FloodFill( hdc, 0, 0, chroma );
\r
1808 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1809 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1810 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1811 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1812 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1814 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1815 but if the start point is not inside the piece we're lost!
\r
1816 There should be a better way to do this... if we could create a region or path
\r
1817 from the fill operation we would be fine for example.
\r
1819 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1820 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1822 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1823 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1824 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1826 SelectObject( dc2, bm2 );
\r
1827 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1828 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1829 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1830 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1831 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1834 DeleteObject( bm2 );
\r
1837 SetTextColor( hdc, 0 );
\r
1839 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1840 draw the piece again in black for safety.
\r
1842 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1844 SelectObject( hdc, hbm_old );
\r
1846 if( hPieceMask[index] != NULL ) {
\r
1847 DeleteObject( hPieceMask[index] );
\r
1850 hPieceMask[index] = hbm;
\r
1853 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1855 SelectObject( hdc, hbm );
\r
1858 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1859 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1860 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1862 SelectObject( dc1, hPieceMask[index] );
\r
1863 SelectObject( dc2, bm2 );
\r
1864 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1865 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1868 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1869 the piece background and deletes (makes transparent) the rest.
\r
1870 Thanks to that mask, we are free to paint the background with the greates
\r
1871 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1872 We use this, to make gradients and give the pieces a "roundish" look.
\r
1874 SetPieceBackground( hdc, backColor, 2 );
\r
1875 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1879 DeleteObject( bm2 );
\r
1882 SetTextColor( hdc, foreColor );
\r
1883 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1885 SelectObject( hdc, hbm_old );
\r
1887 if( hPieceFace[index] != NULL ) {
\r
1888 DeleteObject( hPieceFace[index] );
\r
1891 hPieceFace[index] = hbm;
\r
1894 static int TranslatePieceToFontPiece( int piece )
\r
1924 case BlackMarshall:
\r
1928 case BlackNightrider:
\r
1934 case BlackUnicorn:
\r
1938 case BlackGrasshopper:
\r
1950 case BlackCardinal:
\r
1957 case WhiteMarshall:
\r
1961 case WhiteNightrider:
\r
1967 case WhiteUnicorn:
\r
1971 case WhiteGrasshopper:
\r
1983 case WhiteCardinal:
\r
1992 void CreatePiecesFromFont()
\r
1995 HDC hdc_window = NULL;
\r
2001 if( fontBitmapSquareSize < 0 ) {
\r
2002 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2006 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2007 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2008 fontBitmapSquareSize = -1;
\r
2012 if( fontBitmapSquareSize != squareSize ) {
\r
2013 hdc_window = GetDC( hwndMain );
\r
2014 hdc = CreateCompatibleDC( hdc_window );
\r
2016 if( hPieceFont != NULL ) {
\r
2017 DeleteObject( hPieceFont );
\r
2020 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2021 hPieceMask[i] = NULL;
\r
2022 hPieceFace[i] = NULL;
\r
2028 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2029 fontHeight = appData.fontPieceSize;
\r
2032 fontHeight = (fontHeight * squareSize) / 100;
\r
2034 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2036 lf.lfEscapement = 0;
\r
2037 lf.lfOrientation = 0;
\r
2038 lf.lfWeight = FW_NORMAL;
\r
2040 lf.lfUnderline = 0;
\r
2041 lf.lfStrikeOut = 0;
\r
2042 lf.lfCharSet = DEFAULT_CHARSET;
\r
2043 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2044 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2045 lf.lfQuality = PROOF_QUALITY;
\r
2046 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2047 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2048 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2050 hPieceFont = CreateFontIndirect( &lf );
\r
2052 if( hPieceFont == NULL ) {
\r
2053 fontBitmapSquareSize = -2;
\r
2056 /* Setup font-to-piece character table */
\r
2057 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2058 /* No (or wrong) global settings, try to detect the font */
\r
2059 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2061 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2063 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2064 /* DiagramTT* family */
\r
2065 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2067 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2068 /* Fairy symbols */
\r
2069 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2071 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2072 /* Good Companion (Some characters get warped as literal :-( */
\r
2073 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2074 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2075 SetCharTable(pieceToFontChar, s);
\r
2078 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2079 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2083 /* Create bitmaps */
\r
2084 hfont_old = SelectObject( hdc, hPieceFont );
\r
2085 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2086 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2087 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2089 SelectObject( hdc, hfont_old );
\r
2091 fontBitmapSquareSize = squareSize;
\r
2095 if( hdc != NULL ) {
\r
2099 if( hdc_window != NULL ) {
\r
2100 ReleaseDC( hwndMain, hdc_window );
\r
2105 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2109 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2110 if (gameInfo.event &&
\r
2111 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2112 strcmp(name, "k80s") == 0) {
\r
2113 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2115 return LoadBitmap(hinst, name);
\r
2119 /* Insert a color into the program's logical palette
\r
2120 structure. This code assumes the given color is
\r
2121 the result of the RGB or PALETTERGB macro, and it
\r
2122 knows how those macros work (which is documented).
\r
2125 InsertInPalette(COLORREF color)
\r
2127 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2129 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2130 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2131 pLogPal->palNumEntries--;
\r
2135 pe->peFlags = (char) 0;
\r
2136 pe->peRed = (char) (0xFF & color);
\r
2137 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2138 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2144 InitDrawingColors()
\r
2146 if (pLogPal == NULL) {
\r
2147 /* Allocate enough memory for a logical palette with
\r
2148 * PALETTESIZE entries and set the size and version fields
\r
2149 * of the logical palette structure.
\r
2151 pLogPal = (NPLOGPALETTE)
\r
2152 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2153 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2154 pLogPal->palVersion = 0x300;
\r
2156 pLogPal->palNumEntries = 0;
\r
2158 InsertInPalette(lightSquareColor);
\r
2159 InsertInPalette(darkSquareColor);
\r
2160 InsertInPalette(whitePieceColor);
\r
2161 InsertInPalette(blackPieceColor);
\r
2162 InsertInPalette(highlightSquareColor);
\r
2163 InsertInPalette(premoveHighlightColor);
\r
2165 /* create a logical color palette according the information
\r
2166 * in the LOGPALETTE structure.
\r
2168 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2170 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2171 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2172 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2173 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2174 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2175 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2176 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2177 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2178 /* [AS] Force rendering of the font-based pieces */
\r
2179 if( fontBitmapSquareSize > 0 ) {
\r
2180 fontBitmapSquareSize = 0;
\r
2186 BoardWidth(int boardSize, int n)
\r
2187 { /* [HGM] argument n added to allow different width and height */
\r
2188 int lineGap = sizeInfo[boardSize].lineGap;
\r
2190 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2191 lineGap = appData.overrideLineGap;
\r
2194 return (n + 1) * lineGap +
\r
2195 n * sizeInfo[boardSize].squareSize;
\r
2198 /* Respond to board resize by dragging edge */
\r
2200 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2202 BoardSize newSize = NUM_SIZES - 1;
\r
2203 static int recurse = 0;
\r
2204 if (IsIconic(hwndMain)) return;
\r
2205 if (recurse > 0) return;
\r
2207 while (newSize > 0) {
\r
2208 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2209 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2210 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2213 boardSize = newSize;
\r
2214 InitDrawingSizes(boardSize, flags);
\r
2219 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2222 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2224 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2225 ChessSquare piece;
\r
2226 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2228 SIZE clockSize, messageSize;
\r
2230 char buf[MSG_SIZ];
\r
2232 HMENU hmenu = GetMenu(hwndMain);
\r
2233 RECT crect, wrect, oldRect;
\r
2235 LOGBRUSH logbrush;
\r
2237 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2238 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2240 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2241 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2243 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2244 oldRect.top = wpMain.y;
\r
2245 oldRect.right = wpMain.x + wpMain.width;
\r
2246 oldRect.bottom = wpMain.y + wpMain.height;
\r
2248 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2249 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2250 squareSize = sizeInfo[boardSize].squareSize;
\r
2251 lineGap = sizeInfo[boardSize].lineGap;
\r
2252 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2254 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2255 lineGap = appData.overrideLineGap;
\r
2258 if (tinyLayout != oldTinyLayout) {
\r
2259 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2261 style &= ~WS_SYSMENU;
\r
2262 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2263 "&Minimize\tCtrl+F4");
\r
2265 style |= WS_SYSMENU;
\r
2266 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2268 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2270 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2271 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2272 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2274 DrawMenuBar(hwndMain);
\r
2277 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2278 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2280 /* Get text area sizes */
\r
2281 hdc = GetDC(hwndMain);
\r
2282 if (appData.clockMode) {
\r
2283 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2285 snprintf(buf, MSG_SIZ, _("White"));
\r
2287 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2288 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2289 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2290 str = _("We only care about the height here");
\r
2291 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2292 SelectObject(hdc, oldFont);
\r
2293 ReleaseDC(hwndMain, hdc);
\r
2295 /* Compute where everything goes */
\r
2296 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2297 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2298 logoHeight = 2*clockSize.cy;
\r
2299 leftLogoRect.left = OUTER_MARGIN;
\r
2300 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2301 leftLogoRect.top = OUTER_MARGIN;
\r
2302 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2304 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2305 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2306 rightLogoRect.top = OUTER_MARGIN;
\r
2307 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2310 whiteRect.left = leftLogoRect.right;
\r
2311 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2312 whiteRect.top = OUTER_MARGIN;
\r
2313 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2315 blackRect.right = rightLogoRect.left;
\r
2316 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2317 blackRect.top = whiteRect.top;
\r
2318 blackRect.bottom = whiteRect.bottom;
\r
2320 whiteRect.left = OUTER_MARGIN;
\r
2321 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2322 whiteRect.top = OUTER_MARGIN;
\r
2323 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2325 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2326 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2327 blackRect.top = whiteRect.top;
\r
2328 blackRect.bottom = whiteRect.bottom;
\r
2330 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2333 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2334 if (appData.showButtonBar) {
\r
2335 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2336 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2338 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2340 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2341 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2343 boardRect.left = OUTER_MARGIN;
\r
2344 boardRect.right = boardRect.left + boardWidth;
\r
2345 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2346 boardRect.bottom = boardRect.top + boardHeight;
\r
2348 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2349 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2350 oldBoardSize = boardSize;
\r
2351 oldTinyLayout = tinyLayout;
\r
2352 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2353 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2354 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2355 winW *= 1 + twoBoards;
\r
2356 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2357 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2358 wpMain.height = winH; // without disturbing window attachments
\r
2359 GetWindowRect(hwndMain, &wrect);
\r
2360 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2361 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2363 // [HGM] placement: let attached windows follow size change.
\r
2364 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2365 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2366 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2367 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2368 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2370 /* compensate if menu bar wrapped */
\r
2371 GetClientRect(hwndMain, &crect);
\r
2372 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2373 wpMain.height += offby;
\r
2375 case WMSZ_TOPLEFT:
\r
2376 SetWindowPos(hwndMain, NULL,
\r
2377 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2378 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2381 case WMSZ_TOPRIGHT:
\r
2383 SetWindowPos(hwndMain, NULL,
\r
2384 wrect.left, wrect.bottom - wpMain.height,
\r
2385 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2388 case WMSZ_BOTTOMLEFT:
\r
2390 SetWindowPos(hwndMain, NULL,
\r
2391 wrect.right - wpMain.width, wrect.top,
\r
2392 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2395 case WMSZ_BOTTOMRIGHT:
\r
2399 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2400 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2405 for (i = 0; i < N_BUTTONS; i++) {
\r
2406 if (buttonDesc[i].hwnd != NULL) {
\r
2407 DestroyWindow(buttonDesc[i].hwnd);
\r
2408 buttonDesc[i].hwnd = NULL;
\r
2410 if (appData.showButtonBar) {
\r
2411 buttonDesc[i].hwnd =
\r
2412 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2413 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2414 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2415 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2416 (HMENU) buttonDesc[i].id,
\r
2417 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2419 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2420 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2421 MAKELPARAM(FALSE, 0));
\r
2423 if (buttonDesc[i].id == IDM_Pause)
\r
2424 hwndPause = buttonDesc[i].hwnd;
\r
2425 buttonDesc[i].wndproc = (WNDPROC)
\r
2426 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2429 if (gridPen != NULL) DeleteObject(gridPen);
\r
2430 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2431 if (premovePen != NULL) DeleteObject(premovePen);
\r
2432 if (lineGap != 0) {
\r
2433 logbrush.lbStyle = BS_SOLID;
\r
2434 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2436 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2437 lineGap, &logbrush, 0, NULL);
\r
2438 logbrush.lbColor = highlightSquareColor;
\r
2440 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2441 lineGap, &logbrush, 0, NULL);
\r
2443 logbrush.lbColor = premoveHighlightColor;
\r
2445 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2446 lineGap, &logbrush, 0, NULL);
\r
2448 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2449 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2450 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2451 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2452 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2453 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2454 BOARD_WIDTH * (squareSize + lineGap);
\r
2455 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2457 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2458 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2459 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2460 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2461 lineGap / 2 + (i * (squareSize + lineGap));
\r
2462 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2463 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2464 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2468 /* [HGM] Licensing requirement */
\r
2470 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2473 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2475 GothicPopUp( "", VariantNormal);
\r
2478 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2480 /* Load piece bitmaps for this board size */
\r
2481 for (i=0; i<=2; i++) {
\r
2482 for (piece = WhitePawn;
\r
2483 (int) piece < (int) BlackPawn;
\r
2484 piece = (ChessSquare) ((int) piece + 1)) {
\r
2485 if (pieceBitmap[i][piece] != NULL)
\r
2486 DeleteObject(pieceBitmap[i][piece]);
\r
2490 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2491 // Orthodox Chess pieces
\r
2492 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2493 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2494 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2495 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2496 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2497 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2498 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2499 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2500 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2501 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2502 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2503 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2504 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2505 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2506 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2507 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2508 // in Shogi, Hijack the unused Queen for Lance
\r
2509 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2510 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2511 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2513 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2518 if(squareSize <= 72 && squareSize >= 33) {
\r
2519 /* A & C are available in most sizes now */
\r
2520 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2521 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2524 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2527 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2530 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2533 } else { // Smirf-like
\r
2534 if(gameInfo.variant == VariantSChess) {
\r
2535 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2536 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2537 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2544 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2545 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2548 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2549 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2550 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2551 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2552 } else { // WinBoard standard
\r
2553 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2560 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2561 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2564 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2576 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2585 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2588 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2592 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2593 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2596 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2599 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2600 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2601 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2602 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2603 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2604 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2606 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2609 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2610 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2611 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2612 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2613 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2614 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2615 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2616 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2617 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2620 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2621 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2622 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2623 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2624 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2625 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2626 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2627 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2628 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2629 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2630 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2631 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2632 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2633 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2634 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2638 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2639 /* special Shogi support in this size */
\r
2640 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2641 for (piece = WhitePawn;
\r
2642 (int) piece < (int) BlackPawn;
\r
2643 piece = (ChessSquare) ((int) piece + 1)) {
\r
2644 if (pieceBitmap[i][piece] != NULL)
\r
2645 DeleteObject(pieceBitmap[i][piece]);
\r
2648 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2649 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2650 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2651 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2652 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2653 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2654 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2655 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2656 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2657 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2658 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2659 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2660 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2661 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2662 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2663 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2664 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2665 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2666 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2667 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2668 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2669 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2670 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2671 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2672 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2673 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2674 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2675 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2676 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2677 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2678 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2679 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2680 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2681 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2682 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2683 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2684 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2685 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2686 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2687 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2688 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2689 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2695 PieceBitmap(ChessSquare p, int kind)
\r
2697 if ((int) p >= (int) BlackPawn)
\r
2698 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2700 return pieceBitmap[kind][(int) p];
\r
2703 /***************************************************************/
\r
2705 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2706 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2708 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2709 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2713 SquareToPos(int row, int column, int * x, int * y)
\r
2716 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2717 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2719 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2720 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2725 DrawCoordsOnDC(HDC hdc)
\r
2727 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2728 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2729 char str[2] = { NULLCHAR, NULLCHAR };
\r
2730 int oldMode, oldAlign, x, y, start, i;
\r
2734 if (!appData.showCoords)
\r
2737 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2739 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2740 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2741 oldAlign = GetTextAlign(hdc);
\r
2742 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2744 y = boardRect.top + lineGap;
\r
2745 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2747 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2748 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2749 str[0] = files[start + i];
\r
2750 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2751 y += squareSize + lineGap;
\r
2754 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2756 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2757 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2758 str[0] = ranks[start + i];
\r
2759 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2760 x += squareSize + lineGap;
\r
2763 SelectObject(hdc, oldBrush);
\r
2764 SetBkMode(hdc, oldMode);
\r
2765 SetTextAlign(hdc, oldAlign);
\r
2766 SelectObject(hdc, oldFont);
\r
2770 DrawGridOnDC(HDC hdc)
\r
2774 if (lineGap != 0) {
\r
2775 oldPen = SelectObject(hdc, gridPen);
\r
2776 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2777 SelectObject(hdc, oldPen);
\r
2781 #define HIGHLIGHT_PEN 0
\r
2782 #define PREMOVE_PEN 1
\r
2785 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2788 HPEN oldPen, hPen;
\r
2789 if (lineGap == 0) return;
\r
2791 x1 = boardRect.left +
\r
2792 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2793 y1 = boardRect.top +
\r
2794 lineGap/2 + y * (squareSize + lineGap);
\r
2796 x1 = boardRect.left +
\r
2797 lineGap/2 + x * (squareSize + lineGap);
\r
2798 y1 = boardRect.top +
\r
2799 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2801 hPen = pen ? premovePen : highlightPen;
\r
2802 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2803 MoveToEx(hdc, x1, y1, NULL);
\r
2804 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2805 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2806 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2807 LineTo(hdc, x1, y1);
\r
2808 SelectObject(hdc, oldPen);
\r
2812 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2815 for (i=0; i<2; i++) {
\r
2816 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2817 DrawHighlightOnDC(hdc, TRUE,
\r
2818 h->sq[i].x, h->sq[i].y,
\r
2823 /* Note: sqcolor is used only in monoMode */
\r
2824 /* Note that this code is largely duplicated in woptions.c,
\r
2825 function DrawSampleSquare, so that needs to be updated too */
\r
2827 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2829 HBITMAP oldBitmap;
\r
2833 if (appData.blindfold) return;
\r
2835 /* [AS] Use font-based pieces if needed */
\r
2836 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2837 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2838 CreatePiecesFromFont();
\r
2840 if( fontBitmapSquareSize == squareSize ) {
\r
2841 int index = TranslatePieceToFontPiece(piece);
\r
2843 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2845 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2846 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2850 squareSize, squareSize,
\r
2855 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2857 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2858 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2862 squareSize, squareSize,
\r
2871 if (appData.monoMode) {
\r
2872 SelectObject(tmphdc, PieceBitmap(piece,
\r
2873 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2874 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2875 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2877 tmpSize = squareSize;
\r
2879 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2880 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2881 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2882 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2883 x += (squareSize - minorSize)>>1;
\r
2884 y += squareSize - minorSize - 2;
\r
2885 tmpSize = minorSize;
\r
2887 if (color || appData.allWhite ) {
\r
2888 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2890 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2891 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2892 if(appData.upsideDown && color==flipView)
\r
2893 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2895 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2896 /* Use black for outline of white pieces */
\r
2897 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2898 if(appData.upsideDown && color==flipView)
\r
2899 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2901 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2903 /* Use square color for details of black pieces */
\r
2904 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2905 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2906 if(appData.upsideDown && !flipView)
\r
2907 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2909 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2911 SelectObject(hdc, oldBrush);
\r
2912 SelectObject(tmphdc, oldBitmap);
\r
2916 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2917 int GetBackTextureMode( int algo )
\r
2919 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2923 case BACK_TEXTURE_MODE_PLAIN:
\r
2924 result = 1; /* Always use identity map */
\r
2926 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2927 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2935 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2936 to handle redraws cleanly (as random numbers would always be different).
\r
2938 VOID RebuildTextureSquareInfo()
\r
2948 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2950 if( liteBackTexture != NULL ) {
\r
2951 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2952 lite_w = bi.bmWidth;
\r
2953 lite_h = bi.bmHeight;
\r
2957 if( darkBackTexture != NULL ) {
\r
2958 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2959 dark_w = bi.bmWidth;
\r
2960 dark_h = bi.bmHeight;
\r
2964 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2965 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2966 if( (col + row) & 1 ) {
\r
2968 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2969 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2970 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2972 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2973 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2974 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2976 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2977 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2982 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2983 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2984 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2986 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2987 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2988 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2990 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2991 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2998 /* [AS] Arrow highlighting support */
\r
3000 static double A_WIDTH = 5; /* Width of arrow body */
\r
3002 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3003 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3005 static double Sqr( double x )
\r
3010 static int Round( double x )
\r
3012 return (int) (x + 0.5);
\r
3015 /* Draw an arrow between two points using current settings */
\r
3016 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3019 double dx, dy, j, k, x, y;
\r
3021 if( d_x == s_x ) {
\r
3022 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3024 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3027 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3028 arrow[1].y = d_y - h;
\r
3030 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3031 arrow[2].y = d_y - h;
\r
3036 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3037 arrow[5].y = d_y - h;
\r
3039 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3040 arrow[4].y = d_y - h;
\r
3042 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3045 else if( d_y == s_y ) {
\r
3046 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3049 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3051 arrow[1].x = d_x - w;
\r
3052 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3054 arrow[2].x = d_x - w;
\r
3055 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3060 arrow[5].x = d_x - w;
\r
3061 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3063 arrow[4].x = d_x - w;
\r
3064 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3067 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3070 /* [AS] Needed a lot of paper for this! :-) */
\r
3071 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3072 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3074 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3076 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3081 arrow[0].x = Round(x - j);
\r
3082 arrow[0].y = Round(y + j*dx);
\r
3084 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3085 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3088 x = (double) d_x - k;
\r
3089 y = (double) d_y - k*dy;
\r
3092 x = (double) d_x + k;
\r
3093 y = (double) d_y + k*dy;
\r
3096 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3098 arrow[6].x = Round(x - j);
\r
3099 arrow[6].y = Round(y + j*dx);
\r
3101 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3102 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3104 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3105 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3110 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3111 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3114 Polygon( hdc, arrow, 7 );
\r
3117 /* [AS] Draw an arrow between two squares */
\r
3118 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3120 int s_x, s_y, d_x, d_y;
\r
3127 if( s_col == d_col && s_row == d_row ) {
\r
3131 /* Get source and destination points */
\r
3132 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3133 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3136 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3138 else if( d_y < s_y ) {
\r
3139 d_y += squareSize / 2 + squareSize / 4;
\r
3142 d_y += squareSize / 2;
\r
3146 d_x += squareSize / 2 - squareSize / 4;
\r
3148 else if( d_x < s_x ) {
\r
3149 d_x += squareSize / 2 + squareSize / 4;
\r
3152 d_x += squareSize / 2;
\r
3155 s_x += squareSize / 2;
\r
3156 s_y += squareSize / 2;
\r
3158 /* Adjust width */
\r
3159 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3162 stLB.lbStyle = BS_SOLID;
\r
3163 stLB.lbColor = appData.highlightArrowColor;
\r
3166 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3167 holdpen = SelectObject( hdc, hpen );
\r
3168 hbrush = CreateBrushIndirect( &stLB );
\r
3169 holdbrush = SelectObject( hdc, hbrush );
\r
3171 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3173 SelectObject( hdc, holdpen );
\r
3174 SelectObject( hdc, holdbrush );
\r
3175 DeleteObject( hpen );
\r
3176 DeleteObject( hbrush );
\r
3179 BOOL HasHighlightInfo()
\r
3181 BOOL result = FALSE;
\r
3183 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3184 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3192 BOOL IsDrawArrowEnabled()
\r
3194 BOOL result = FALSE;
\r
3196 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3203 VOID DrawArrowHighlight( HDC hdc )
\r
3205 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3206 DrawArrowBetweenSquares( hdc,
\r
3207 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3208 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3212 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3214 HRGN result = NULL;
\r
3216 if( HasHighlightInfo() ) {
\r
3217 int x1, y1, x2, y2;
\r
3218 int sx, sy, dx, dy;
\r
3220 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3221 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3223 sx = MIN( x1, x2 );
\r
3224 sy = MIN( y1, y2 );
\r
3225 dx = MAX( x1, x2 ) + squareSize;
\r
3226 dy = MAX( y1, y2 ) + squareSize;
\r
3228 result = CreateRectRgn( sx, sy, dx, dy );
\r
3235 Warning: this function modifies the behavior of several other functions.
\r
3237 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3238 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3239 repaint is scattered all over the place, which is not good for features such as
\r
3240 "arrow highlighting" that require a full repaint of the board.
\r
3242 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3243 user interaction, when speed is not so important) but especially to avoid errors
\r
3244 in the displayed graphics.
\r
3246 In such patched places, I always try refer to this function so there is a single
\r
3247 place to maintain knowledge.
\r
3249 To restore the original behavior, just return FALSE unconditionally.
\r
3251 BOOL IsFullRepaintPreferrable()
\r
3253 BOOL result = FALSE;
\r
3255 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3256 /* Arrow may appear on the board */
\r
3264 This function is called by DrawPosition to know whether a full repaint must
\r
3267 Only DrawPosition may directly call this function, which makes use of
\r
3268 some state information. Other function should call DrawPosition specifying
\r
3269 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3271 BOOL DrawPositionNeedsFullRepaint()
\r
3273 BOOL result = FALSE;
\r
3276 Probably a slightly better policy would be to trigger a full repaint
\r
3277 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3278 but animation is fast enough that it's difficult to notice.
\r
3280 if( animInfo.piece == EmptySquare ) {
\r
3281 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3290 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3292 int row, column, x, y, square_color, piece_color;
\r
3293 ChessSquare piece;
\r
3295 HDC texture_hdc = NULL;
\r
3297 /* [AS] Initialize background textures if needed */
\r
3298 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3299 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3300 if( backTextureSquareSize != squareSize
\r
3301 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3302 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3303 backTextureSquareSize = squareSize;
\r
3304 RebuildTextureSquareInfo();
\r
3307 texture_hdc = CreateCompatibleDC( hdc );
\r
3310 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3311 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3313 SquareToPos(row, column, &x, &y);
\r
3315 piece = board[row][column];
\r
3317 square_color = ((column + row) % 2) == 1;
\r
3318 if( gameInfo.variant == VariantXiangqi ) {
\r
3319 square_color = !InPalace(row, column);
\r
3320 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3321 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3323 piece_color = (int) piece < (int) BlackPawn;
\r
3326 /* [HGM] holdings file: light square or black */
\r
3327 if(column == BOARD_LEFT-2) {
\r
3328 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3331 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3335 if(column == BOARD_RGHT + 1 ) {
\r
3336 if( row < gameInfo.holdingsSize )
\r
3339 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3343 if(column == BOARD_LEFT-1 ) /* left align */
\r
3344 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3345 else if( column == BOARD_RGHT) /* right align */
\r
3346 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3348 if (appData.monoMode) {
\r
3349 if (piece == EmptySquare) {
\r
3350 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3351 square_color ? WHITENESS : BLACKNESS);
\r
3353 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3356 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3357 /* [AS] Draw the square using a texture bitmap */
\r
3358 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3359 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3360 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3363 squareSize, squareSize,
\r
3366 backTextureSquareInfo[r][c].mode,
\r
3367 backTextureSquareInfo[r][c].x,
\r
3368 backTextureSquareInfo[r][c].y );
\r
3370 SelectObject( texture_hdc, hbm );
\r
3372 if (piece != EmptySquare) {
\r
3373 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3377 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3379 oldBrush = SelectObject(hdc, brush );
\r
3380 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3381 SelectObject(hdc, oldBrush);
\r
3382 if (piece != EmptySquare)
\r
3383 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3388 if( texture_hdc != NULL ) {
\r
3389 DeleteDC( texture_hdc );
\r
3393 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3394 void fputDW(FILE *f, int x)
\r
3396 fputc(x & 255, f);
\r
3397 fputc(x>>8 & 255, f);
\r
3398 fputc(x>>16 & 255, f);
\r
3399 fputc(x>>24 & 255, f);
\r
3402 #define MAX_CLIPS 200 /* more than enough */
\r
3405 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3407 // HBITMAP bufferBitmap;
\r
3412 int w = 100, h = 50;
\r
3414 if(logo == NULL) {
\r
3415 if(!logoHeight) return;
\r
3416 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3418 // GetClientRect(hwndMain, &Rect);
\r
3419 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3420 // Rect.bottom-Rect.top+1);
\r
3421 tmphdc = CreateCompatibleDC(hdc);
\r
3422 hbm = SelectObject(tmphdc, logo);
\r
3423 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3427 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3428 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3429 SelectObject(tmphdc, hbm);
\r
3437 HDC hdc = GetDC(hwndMain);
\r
3438 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3439 if(appData.autoLogo) {
\r
3441 switch(gameMode) { // pick logos based on game mode
\r
3442 case IcsObserving:
\r
3443 whiteLogo = second.programLogo; // ICS logo
\r
3444 blackLogo = second.programLogo;
\r
3447 case IcsPlayingWhite:
\r
3448 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3449 blackLogo = second.programLogo; // ICS logo
\r
3451 case IcsPlayingBlack:
\r
3452 whiteLogo = second.programLogo; // ICS logo
\r
3453 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3455 case TwoMachinesPlay:
\r
3456 if(first.twoMachinesColor[0] == 'b') {
\r
3457 whiteLogo = second.programLogo;
\r
3458 blackLogo = first.programLogo;
\r
3461 case MachinePlaysWhite:
\r
3462 blackLogo = userLogo;
\r
3464 case MachinePlaysBlack:
\r
3465 whiteLogo = userLogo;
\r
3466 blackLogo = first.programLogo;
\r
3469 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3470 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3471 ReleaseDC(hwndMain, hdc);
\r
3476 UpdateLogos(int display)
\r
3477 { // called after loading new engine(s), in tourney or from menu
\r
3478 LoadLogo(&first, 0, FALSE);
\r
3479 LoadLogo(&second, 1, appData.icsActive);
\r
3480 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3481 if(display) DisplayLogos();
\r
3484 static HDC hdcSeek;
\r
3486 // [HGM] seekgraph
\r
3487 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3490 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3491 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3492 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3493 SelectObject( hdcSeek, hp );
\r
3496 // front-end wrapper for drawing functions to do rectangles
\r
3497 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3502 if (hdcSeek == NULL) {
\r
3503 hdcSeek = GetDC(hwndMain);
\r
3504 if (!appData.monoMode) {
\r
3505 SelectPalette(hdcSeek, hPal, FALSE);
\r
3506 RealizePalette(hdcSeek);
\r
3509 hp = SelectObject( hdcSeek, gridPen );
\r
3510 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3511 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3512 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3513 SelectObject( hdcSeek, hp );
\r
3516 // front-end wrapper for putting text in graph
\r
3517 void DrawSeekText(char *buf, int x, int y)
\r
3520 SetBkMode( hdcSeek, TRANSPARENT );
\r
3521 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3522 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3525 void DrawSeekDot(int x, int y, int color)
\r
3527 int square = color & 0x80;
\r
3528 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3529 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3532 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3533 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3535 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3536 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3537 SelectObject(hdcSeek, oldBrush);
\r
3541 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3543 static Board lastReq[2], lastDrawn[2];
\r
3544 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3545 static int lastDrawnFlipView = 0;
\r
3546 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3547 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3550 HBITMAP bufferBitmap;
\r
3551 HBITMAP oldBitmap;
\r
3553 HRGN clips[MAX_CLIPS];
\r
3554 ChessSquare dragged_piece = EmptySquare;
\r
3555 int nr = twoBoards*partnerUp;
\r
3557 /* I'm undecided on this - this function figures out whether a full
\r
3558 * repaint is necessary on its own, so there's no real reason to have the
\r
3559 * caller tell it that. I think this can safely be set to FALSE - but
\r
3560 * if we trust the callers not to request full repaints unnessesarily, then
\r
3561 * we could skip some clipping work. In other words, only request a full
\r
3562 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3563 * gamestart and similar) --Hawk
\r
3565 Boolean fullrepaint = repaint;
\r
3567 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3569 if( DrawPositionNeedsFullRepaint() ) {
\r
3570 fullrepaint = TRUE;
\r
3573 if (board == NULL) {
\r
3574 if (!lastReqValid[nr]) {
\r
3577 board = lastReq[nr];
\r
3579 CopyBoard(lastReq[nr], board);
\r
3580 lastReqValid[nr] = 1;
\r
3583 if (doingSizing) {
\r
3587 if (IsIconic(hwndMain)) {
\r
3591 if (hdc == NULL) {
\r
3592 hdc = GetDC(hwndMain);
\r
3593 if (!appData.monoMode) {
\r
3594 SelectPalette(hdc, hPal, FALSE);
\r
3595 RealizePalette(hdc);
\r
3599 releaseDC = FALSE;
\r
3602 /* Create some work-DCs */
\r
3603 hdcmem = CreateCompatibleDC(hdc);
\r
3604 tmphdc = CreateCompatibleDC(hdc);
\r
3606 /* If dragging is in progress, we temporarely remove the piece */
\r
3607 /* [HGM] or temporarily decrease count if stacked */
\r
3608 /* !! Moved to before board compare !! */
\r
3609 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3610 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3611 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3612 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3613 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3615 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3616 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3617 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3619 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3622 /* Figure out which squares need updating by comparing the
\r
3623 * newest board with the last drawn board and checking if
\r
3624 * flipping has changed.
\r
3626 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3627 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3628 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3629 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3630 SquareToPos(row, column, &x, &y);
\r
3631 clips[num_clips++] =
\r
3632 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3636 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3637 for (i=0; i<2; i++) {
\r
3638 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3639 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3640 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3641 lastDrawnHighlight.sq[i].y >= 0) {
\r
3642 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3643 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3644 clips[num_clips++] =
\r
3645 CreateRectRgn(x - lineGap, y - lineGap,
\r
3646 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3648 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3649 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3650 clips[num_clips++] =
\r
3651 CreateRectRgn(x - lineGap, y - lineGap,
\r
3652 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3656 for (i=0; i<2; i++) {
\r
3657 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3658 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3659 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3660 lastDrawnPremove.sq[i].y >= 0) {
\r
3661 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3662 lastDrawnPremove.sq[i].x, &x, &y);
\r
3663 clips[num_clips++] =
\r
3664 CreateRectRgn(x - lineGap, y - lineGap,
\r
3665 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3667 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3668 premoveHighlightInfo.sq[i].y >= 0) {
\r
3669 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3670 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3671 clips[num_clips++] =
\r
3672 CreateRectRgn(x - lineGap, y - lineGap,
\r
3673 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3677 } else { // nr == 1
\r
3678 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3679 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3680 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3681 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3682 for (i=0; i<2; i++) {
\r
3683 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3684 partnerHighlightInfo.sq[i].y >= 0) {
\r
3685 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3686 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3687 clips[num_clips++] =
\r
3688 CreateRectRgn(x - lineGap, y - lineGap,
\r
3689 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3691 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3692 oldPartnerHighlight.sq[i].y >= 0) {
\r
3693 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3694 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3695 clips[num_clips++] =
\r
3696 CreateRectRgn(x - lineGap, y - lineGap,
\r
3697 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3702 fullrepaint = TRUE;
\r
3705 /* Create a buffer bitmap - this is the actual bitmap
\r
3706 * being written to. When all the work is done, we can
\r
3707 * copy it to the real DC (the screen). This avoids
\r
3708 * the problems with flickering.
\r
3710 GetClientRect(hwndMain, &Rect);
\r
3711 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3712 Rect.bottom-Rect.top+1);
\r
3713 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3714 if (!appData.monoMode) {
\r
3715 SelectPalette(hdcmem, hPal, FALSE);
\r
3718 /* Create clips for dragging */
\r
3719 if (!fullrepaint) {
\r
3720 if (dragInfo.from.x >= 0) {
\r
3721 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3722 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3724 if (dragInfo.start.x >= 0) {
\r
3725 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3726 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3728 if (dragInfo.pos.x >= 0) {
\r
3729 x = dragInfo.pos.x - squareSize / 2;
\r
3730 y = dragInfo.pos.y - squareSize / 2;
\r
3731 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3733 if (dragInfo.lastpos.x >= 0) {
\r
3734 x = dragInfo.lastpos.x - squareSize / 2;
\r
3735 y = dragInfo.lastpos.y - squareSize / 2;
\r
3736 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3740 /* Are we animating a move?
\r
3742 * - remove the piece from the board (temporarely)
\r
3743 * - calculate the clipping region
\r
3745 if (!fullrepaint) {
\r
3746 if (animInfo.piece != EmptySquare) {
\r
3747 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3748 x = boardRect.left + animInfo.lastpos.x;
\r
3749 y = boardRect.top + animInfo.lastpos.y;
\r
3750 x2 = boardRect.left + animInfo.pos.x;
\r
3751 y2 = boardRect.top + animInfo.pos.y;
\r
3752 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3753 /* Slight kludge. The real problem is that after AnimateMove is
\r
3754 done, the position on the screen does not match lastDrawn.
\r
3755 This currently causes trouble only on e.p. captures in
\r
3756 atomic, where the piece moves to an empty square and then
\r
3757 explodes. The old and new positions both had an empty square
\r
3758 at the destination, but animation has drawn a piece there and
\r
3759 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3760 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3764 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3765 if (num_clips == 0)
\r
3766 fullrepaint = TRUE;
\r
3768 /* Set clipping on the memory DC */
\r
3769 if (!fullrepaint) {
\r
3770 SelectClipRgn(hdcmem, clips[0]);
\r
3771 for (x = 1; x < num_clips; x++) {
\r
3772 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3773 abort(); // this should never ever happen!
\r
3777 /* Do all the drawing to the memory DC */
\r
3778 if(explodeInfo.radius) { // [HGM] atomic
\r
3780 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3781 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3782 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3783 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3784 x += squareSize/2;
\r
3785 y += squareSize/2;
\r
3786 if(!fullrepaint) {
\r
3787 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3788 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3790 DrawGridOnDC(hdcmem);
\r
3791 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3792 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3793 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3794 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3795 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3796 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3797 SelectObject(hdcmem, oldBrush);
\r
3799 DrawGridOnDC(hdcmem);
\r
3800 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3801 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3802 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3804 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3805 oldPartnerHighlight = partnerHighlightInfo;
\r
3807 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3809 if(nr == 0) // [HGM] dual: markers only on left board
\r
3810 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3811 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3812 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3813 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3814 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3815 SquareToPos(row, column, &x, &y);
\r
3816 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3817 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3818 SelectObject(hdcmem, oldBrush);
\r
3823 if( appData.highlightMoveWithArrow ) {
\r
3824 DrawArrowHighlight(hdcmem);
\r
3827 DrawCoordsOnDC(hdcmem);
\r
3829 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3830 /* to make sure lastDrawn contains what is actually drawn */
\r
3832 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3833 if (dragged_piece != EmptySquare) {
\r
3834 /* [HGM] or restack */
\r
3835 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3836 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3838 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3839 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3840 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3841 x = dragInfo.pos.x - squareSize / 2;
\r
3842 y = dragInfo.pos.y - squareSize / 2;
\r
3843 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3844 ((int) dragInfo.piece < (int) BlackPawn),
\r
3845 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3848 /* Put the animated piece back into place and draw it */
\r
3849 if (animInfo.piece != EmptySquare) {
\r
3850 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3851 x = boardRect.left + animInfo.pos.x;
\r
3852 y = boardRect.top + animInfo.pos.y;
\r
3853 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3854 ((int) animInfo.piece < (int) BlackPawn),
\r
3855 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3858 /* Release the bufferBitmap by selecting in the old bitmap
\r
3859 * and delete the memory DC
\r
3861 SelectObject(hdcmem, oldBitmap);
\r
3864 /* Set clipping on the target DC */
\r
3865 if (!fullrepaint) {
\r
3866 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3868 GetRgnBox(clips[x], &rect);
\r
3869 DeleteObject(clips[x]);
\r
3870 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3871 rect.right + wpMain.width/2, rect.bottom);
\r
3873 SelectClipRgn(hdc, clips[0]);
\r
3874 for (x = 1; x < num_clips; x++) {
\r
3875 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3876 abort(); // this should never ever happen!
\r
3880 /* Copy the new bitmap onto the screen in one go.
\r
3881 * This way we avoid any flickering
\r
3883 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3884 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3885 boardRect.right - boardRect.left,
\r
3886 boardRect.bottom - boardRect.top,
\r
3887 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3888 if(saveDiagFlag) {
\r
3889 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3890 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3892 GetObject(bufferBitmap, sizeof(b), &b);
\r
3893 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3894 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3895 bih.biWidth = b.bmWidth;
\r
3896 bih.biHeight = b.bmHeight;
\r
3898 bih.biBitCount = b.bmBitsPixel;
\r
3899 bih.biCompression = 0;
\r
3900 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3901 bih.biXPelsPerMeter = 0;
\r
3902 bih.biYPelsPerMeter = 0;
\r
3903 bih.biClrUsed = 0;
\r
3904 bih.biClrImportant = 0;
\r
3905 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3906 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3907 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3908 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3910 wb = b.bmWidthBytes;
\r
3912 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3913 int k = ((int*) pData)[i];
\r
3914 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3915 if(j >= 16) break;
\r
3917 if(j >= nrColors) nrColors = j+1;
\r
3919 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3921 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3922 for(w=0; w<(wb>>2); w+=2) {
\r
3923 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3924 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3925 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3926 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3927 pData[p++] = m | j<<4;
\r
3929 while(p&3) pData[p++] = 0;
\r
3932 wb = ((wb+31)>>5)<<2;
\r
3934 // write BITMAPFILEHEADER
\r
3935 fprintf(diagFile, "BM");
\r
3936 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3937 fputDW(diagFile, 0);
\r
3938 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3939 // write BITMAPINFOHEADER
\r
3940 fputDW(diagFile, 40);
\r
3941 fputDW(diagFile, b.bmWidth);
\r
3942 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3943 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3944 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3945 fputDW(diagFile, 0);
\r
3946 fputDW(diagFile, 0);
\r
3947 fputDW(diagFile, 0);
\r
3948 fputDW(diagFile, 0);
\r
3949 fputDW(diagFile, 0);
\r
3950 fputDW(diagFile, 0);
\r
3951 // write color table
\r
3953 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3954 // write bitmap data
\r
3955 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3956 fputc(pData[i], diagFile);
\r
3961 SelectObject(tmphdc, oldBitmap);
\r
3963 /* Massive cleanup */
\r
3964 for (x = 0; x < num_clips; x++)
\r
3965 DeleteObject(clips[x]);
\r
3968 DeleteObject(bufferBitmap);
\r
3971 ReleaseDC(hwndMain, hdc);
\r
3973 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3975 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3977 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3980 /* CopyBoard(lastDrawn, board);*/
\r
3981 lastDrawnHighlight = highlightInfo;
\r
3982 lastDrawnPremove = premoveHighlightInfo;
\r
3983 lastDrawnFlipView = flipView;
\r
3984 lastDrawnValid[nr] = 1;
\r
3987 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3992 saveDiagFlag = 1; diagFile = f;
\r
3993 HDCDrawPosition(NULL, TRUE, NULL);
\r
4001 /*---------------------------------------------------------------------------*\
\r
4002 | CLIENT PAINT PROCEDURE
\r
4003 | This is the main event-handler for the WM_PAINT message.
\r
4005 \*---------------------------------------------------------------------------*/
\r
4007 PaintProc(HWND hwnd)
\r
4013 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4014 if (IsIconic(hwnd)) {
\r
4015 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4017 if (!appData.monoMode) {
\r
4018 SelectPalette(hdc, hPal, FALSE);
\r
4019 RealizePalette(hdc);
\r
4021 HDCDrawPosition(hdc, 1, NULL);
\r
4022 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4023 flipView = !flipView; partnerUp = !partnerUp;
\r
4024 HDCDrawPosition(hdc, 1, NULL);
\r
4025 flipView = !flipView; partnerUp = !partnerUp;
\r
4028 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4029 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4030 ETO_CLIPPED|ETO_OPAQUE,
\r
4031 &messageRect, messageText, strlen(messageText), NULL);
\r
4032 SelectObject(hdc, oldFont);
\r
4033 DisplayBothClocks();
\r
4036 EndPaint(hwnd,&ps);
\r
4044 * If the user selects on a border boundary, return -1; if off the board,
\r
4045 * return -2. Otherwise map the event coordinate to the square.
\r
4046 * The offset boardRect.left or boardRect.top must already have been
\r
4047 * subtracted from x.
\r
4049 int EventToSquare(x, limit)
\r
4057 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4059 x /= (squareSize + lineGap);
\r
4071 DropEnable dropEnables[] = {
\r
4072 { 'P', DP_Pawn, N_("Pawn") },
\r
4073 { 'N', DP_Knight, N_("Knight") },
\r
4074 { 'B', DP_Bishop, N_("Bishop") },
\r
4075 { 'R', DP_Rook, N_("Rook") },
\r
4076 { 'Q', DP_Queen, N_("Queen") },
\r
4080 SetupDropMenu(HMENU hmenu)
\r
4082 int i, count, enable;
\r
4084 extern char white_holding[], black_holding[];
\r
4085 char item[MSG_SIZ];
\r
4087 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4088 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4089 dropEnables[i].piece);
\r
4091 while (p && *p++ == dropEnables[i].piece) count++;
\r
4092 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4093 enable = count > 0 || !appData.testLegality
\r
4094 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4095 && !appData.icsActive);
\r
4096 ModifyMenu(hmenu, dropEnables[i].command,
\r
4097 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4098 dropEnables[i].command, item);
\r
4102 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4104 dragInfo.lastpos.x = boardRect.left + x;
\r
4105 dragInfo.lastpos.y = boardRect.top + y;
\r
4106 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4107 dragInfo.from.x = fromX;
\r
4108 dragInfo.from.y = fromY;
\r
4109 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4110 dragInfo.start = dragInfo.from;
\r
4111 SetCapture(hwndMain);
\r
4114 void DragPieceEnd(int x, int y)
\r
4117 dragInfo.start.x = dragInfo.start.y = -1;
\r
4118 dragInfo.from = dragInfo.start;
\r
4119 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4122 void ChangeDragPiece(ChessSquare piece)
\r
4124 dragInfo.piece = piece;
\r
4127 /* Event handler for mouse messages */
\r
4129 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4133 static int recursive = 0;
\r
4135 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4138 if (message == WM_MBUTTONUP) {
\r
4139 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4140 to the middle button: we simulate pressing the left button too!
\r
4142 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4143 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4149 pt.x = LOWORD(lParam);
\r
4150 pt.y = HIWORD(lParam);
\r
4151 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4152 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4153 if (!flipView && y >= 0) {
\r
4154 y = BOARD_HEIGHT - 1 - y;
\r
4156 if (flipView && x >= 0) {
\r
4157 x = BOARD_WIDTH - 1 - x;
\r
4160 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4162 switch (message) {
\r
4163 case WM_LBUTTONDOWN:
\r
4164 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4165 ClockClick(flipClock); break;
\r
4166 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4167 ClockClick(!flipClock); break;
\r
4169 dragInfo.start.x = dragInfo.start.y = -1;
\r
4170 dragInfo.from = dragInfo.start;
\r
4171 if(fromX == -1 && frozen) { // not sure where this is for
\r
4172 fromX = fromY = -1;
\r
4173 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4176 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4177 DrawPosition(TRUE, NULL);
\r
4180 case WM_LBUTTONUP:
\r
4181 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4182 DrawPosition(TRUE, NULL);
\r
4185 case WM_MOUSEMOVE:
\r
4186 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4187 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4188 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4189 if ((appData.animateDragging || appData.highlightDragging)
\r
4190 && (wParam & MK_LBUTTON)
\r
4191 && dragInfo.from.x >= 0)
\r
4193 BOOL full_repaint = FALSE;
\r
4195 if (appData.animateDragging) {
\r
4196 dragInfo.pos = pt;
\r
4198 if (appData.highlightDragging) {
\r
4199 SetHighlights(fromX, fromY, x, y);
\r
4200 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4201 full_repaint = TRUE;
\r
4205 DrawPosition( full_repaint, NULL);
\r
4207 dragInfo.lastpos = dragInfo.pos;
\r
4211 case WM_MOUSEWHEEL: // [DM]
\r
4212 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4213 /* Mouse Wheel is being rolled forward
\r
4214 * Play moves forward
\r
4216 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4217 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4218 /* Mouse Wheel is being rolled backward
\r
4219 * Play moves backward
\r
4221 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4222 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4226 case WM_MBUTTONUP:
\r
4227 case WM_RBUTTONUP:
\r
4229 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4232 case WM_MBUTTONDOWN:
\r
4233 case WM_RBUTTONDOWN:
\r
4236 fromX = fromY = -1;
\r
4237 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4238 dragInfo.start.x = dragInfo.start.y = -1;
\r
4239 dragInfo.from = dragInfo.start;
\r
4240 dragInfo.lastpos = dragInfo.pos;
\r
4241 if (appData.highlightDragging) {
\r
4242 ClearHighlights();
\r
4245 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4246 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4247 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4248 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4249 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4253 DrawPosition(TRUE, NULL);
\r
4255 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4258 if (message == WM_MBUTTONDOWN) {
\r
4259 buttonCount = 3; /* even if system didn't think so */
\r
4260 if (wParam & MK_SHIFT)
\r
4261 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4263 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4264 } else { /* message == WM_RBUTTONDOWN */
\r
4265 /* Just have one menu, on the right button. Windows users don't
\r
4266 think to try the middle one, and sometimes other software steals
\r
4267 it, or it doesn't really exist. */
\r
4268 if(gameInfo.variant != VariantShogi)
\r
4269 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4271 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4275 SetCapture(hwndMain);
\r
4278 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4279 SetupDropMenu(hmenu);
\r
4280 MenuPopup(hwnd, pt, hmenu, -1);
\r
4290 /* Preprocess messages for buttons in main window */
\r
4292 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4294 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4297 for (i=0; i<N_BUTTONS; i++) {
\r
4298 if (buttonDesc[i].id == id) break;
\r
4300 if (i == N_BUTTONS) return 0;
\r
4301 switch (message) {
\r
4306 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4307 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4314 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4317 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4318 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4319 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4320 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4322 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4324 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4325 TypeInEvent((char)wParam);
\r
4331 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4334 /* Process messages for Promotion dialog box */
\r
4336 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4340 switch (message) {
\r
4341 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4342 /* Center the dialog over the application window */
\r
4343 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4344 Translate(hDlg, DLG_PromotionKing);
\r
4345 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4346 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4347 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4348 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4349 SW_SHOW : SW_HIDE);
\r
4350 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4351 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4352 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4353 PieceToChar(WhiteAngel) != '~') ||
\r
4354 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4355 PieceToChar(BlackAngel) != '~') ) ?
\r
4356 SW_SHOW : SW_HIDE);
\r
4357 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4358 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4359 PieceToChar(WhiteMarshall) != '~') ||
\r
4360 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4361 PieceToChar(BlackMarshall) != '~') ) ?
\r
4362 SW_SHOW : SW_HIDE);
\r
4363 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4364 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4365 gameInfo.variant != VariantShogi ?
\r
4366 SW_SHOW : SW_HIDE);
\r
4367 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4368 gameInfo.variant != VariantShogi ?
\r
4369 SW_SHOW : SW_HIDE);
\r
4370 if(gameInfo.variant == VariantShogi) {
\r
4371 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4372 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4373 SetWindowText(hDlg, "Promote?");
\r
4375 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4376 gameInfo.variant == VariantSuper ?
\r
4377 SW_SHOW : SW_HIDE);
\r
4380 case WM_COMMAND: /* message: received a command */
\r
4381 switch (LOWORD(wParam)) {
\r
4383 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4384 ClearHighlights();
\r
4385 DrawPosition(FALSE, NULL);
\r
4388 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4391 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4394 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4395 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4398 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4399 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4401 case PB_Chancellor:
\r
4402 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4404 case PB_Archbishop:
\r
4405 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4408 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4413 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4414 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4415 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4416 fromX = fromY = -1;
\r
4417 if (!appData.highlightLastMove) {
\r
4418 ClearHighlights();
\r
4419 DrawPosition(FALSE, NULL);
\r
4426 /* Pop up promotion dialog */
\r
4428 PromotionPopup(HWND hwnd)
\r
4432 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4433 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4434 hwnd, (DLGPROC)lpProc);
\r
4435 FreeProcInstance(lpProc);
\r
4441 DrawPosition(TRUE, NULL);
\r
4442 PromotionPopup(hwndMain);
\r
4445 /* Toggle ShowThinking */
\r
4447 ToggleShowThinking()
\r
4449 appData.showThinking = !appData.showThinking;
\r
4450 ShowThinkingEvent();
\r
4454 LoadGameDialog(HWND hwnd, char* title)
\r
4458 char fileTitle[MSG_SIZ];
\r
4459 f = OpenFileDialog(hwnd, "rb", "",
\r
4460 appData.oldSaveStyle ? "gam" : "pgn",
\r
4462 title, &number, fileTitle, NULL);
\r
4464 cmailMsgLoaded = FALSE;
\r
4465 if (number == 0) {
\r
4466 int error = GameListBuild(f);
\r
4468 DisplayError(_("Cannot build game list"), error);
\r
4469 } else if (!ListEmpty(&gameList) &&
\r
4470 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4471 GameListPopUp(f, fileTitle);
\r
4474 GameListDestroy();
\r
4477 LoadGame(f, number, fileTitle, FALSE);
\r
4481 int get_term_width()
\r
4486 HFONT hfont, hold_font;
\r
4491 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4495 // get the text metrics
\r
4496 hdc = GetDC(hText);
\r
4497 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4498 if (consoleCF.dwEffects & CFE_BOLD)
\r
4499 lf.lfWeight = FW_BOLD;
\r
4500 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4501 lf.lfItalic = TRUE;
\r
4502 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4503 lf.lfStrikeOut = TRUE;
\r
4504 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4505 lf.lfUnderline = TRUE;
\r
4506 hfont = CreateFontIndirect(&lf);
\r
4507 hold_font = SelectObject(hdc, hfont);
\r
4508 GetTextMetrics(hdc, &tm);
\r
4509 SelectObject(hdc, hold_font);
\r
4510 DeleteObject(hfont);
\r
4511 ReleaseDC(hText, hdc);
\r
4513 // get the rectangle
\r
4514 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4516 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4519 void UpdateICSWidth(HWND hText)
\r
4521 LONG old_width, new_width;
\r
4523 new_width = get_term_width(hText, FALSE);
\r
4524 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4525 if (new_width != old_width)
\r
4527 ics_update_width(new_width);
\r
4528 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4533 ChangedConsoleFont()
\r
4536 CHARRANGE tmpsel, sel;
\r
4537 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4538 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4539 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4542 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4543 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4544 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4545 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4546 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4547 * size. This was undocumented in the version of MSVC++ that I had
\r
4548 * when I wrote the code, but is apparently documented now.
\r
4550 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4551 cfmt.bCharSet = f->lf.lfCharSet;
\r
4552 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4553 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4554 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4555 /* Why are the following seemingly needed too? */
\r
4556 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4557 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4558 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4560 tmpsel.cpMax = -1; /*999999?*/
\r
4561 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4562 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4563 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4564 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4566 paraf.cbSize = sizeof(paraf);
\r
4567 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4568 paraf.dxStartIndent = 0;
\r
4569 paraf.dxOffset = WRAP_INDENT;
\r
4570 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4571 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4572 UpdateICSWidth(hText);
\r
4575 /*---------------------------------------------------------------------------*\
\r
4577 * Window Proc for main window
\r
4579 \*---------------------------------------------------------------------------*/
\r
4581 /* Process messages for main window, etc. */
\r
4583 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4586 int wmId, wmEvent;
\r
4590 char fileTitle[MSG_SIZ];
\r
4591 char buf[MSG_SIZ];
\r
4592 static SnapData sd;
\r
4593 static int peek=0;
\r
4595 switch (message) {
\r
4597 case WM_PAINT: /* message: repaint portion of window */
\r
4601 case WM_ERASEBKGND:
\r
4602 if (IsIconic(hwnd)) {
\r
4603 /* Cheat; change the message */
\r
4604 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4606 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4610 case WM_LBUTTONDOWN:
\r
4611 case WM_MBUTTONDOWN:
\r
4612 case WM_RBUTTONDOWN:
\r
4613 case WM_LBUTTONUP:
\r
4614 case WM_MBUTTONUP:
\r
4615 case WM_RBUTTONUP:
\r
4616 case WM_MOUSEMOVE:
\r
4617 case WM_MOUSEWHEEL:
\r
4618 MouseEvent(hwnd, message, wParam, lParam);
\r
4622 if((char)wParam == '\b') {
\r
4623 ForwardEvent(); peek = 0;
\r
4626 JAWS_KBUP_NAVIGATION
\r
4631 if((char)wParam == '\b') {
\r
4632 if(!peek) BackwardEvent(), peek = 1;
\r
4635 JAWS_KBDOWN_NAVIGATION
\r
4641 JAWS_ALT_INTERCEPT
\r
4643 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4644 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4645 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4646 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4648 SendMessage(h, message, wParam, lParam);
\r
4649 } else if(lParam != KF_REPEAT) {
\r
4650 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4651 TypeInEvent((char)wParam);
\r
4652 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4653 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4658 case WM_PALETTECHANGED:
\r
4659 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4661 HDC hdc = GetDC(hwndMain);
\r
4662 SelectPalette(hdc, hPal, TRUE);
\r
4663 nnew = RealizePalette(hdc);
\r
4665 paletteChanged = TRUE;
\r
4666 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4668 ReleaseDC(hwnd, hdc);
\r
4672 case WM_QUERYNEWPALETTE:
\r
4673 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4675 HDC hdc = GetDC(hwndMain);
\r
4676 paletteChanged = FALSE;
\r
4677 SelectPalette(hdc, hPal, FALSE);
\r
4678 nnew = RealizePalette(hdc);
\r
4680 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4682 ReleaseDC(hwnd, hdc);
\r
4687 case WM_COMMAND: /* message: command from application menu */
\r
4688 wmId = LOWORD(wParam);
\r
4689 wmEvent = HIWORD(wParam);
\r
4694 SAY("new game enter a move to play against the computer with white");
\r
4697 case IDM_NewGameFRC:
\r
4698 if( NewGameFRC() == 0 ) {
\r
4703 case IDM_NewVariant:
\r
4704 NewVariantPopup(hwnd);
\r
4707 case IDM_LoadGame:
\r
4708 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4711 case IDM_LoadNextGame:
\r
4715 case IDM_LoadPrevGame:
\r
4719 case IDM_ReloadGame:
\r
4723 case IDM_LoadPosition:
\r
4724 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4725 Reset(FALSE, TRUE);
\r
4728 f = OpenFileDialog(hwnd, "rb", "",
\r
4729 appData.oldSaveStyle ? "pos" : "fen",
\r
4731 _("Load Position from File"), &number, fileTitle, NULL);
\r
4733 LoadPosition(f, number, fileTitle);
\r
4737 case IDM_LoadNextPosition:
\r
4738 ReloadPosition(1);
\r
4741 case IDM_LoadPrevPosition:
\r
4742 ReloadPosition(-1);
\r
4745 case IDM_ReloadPosition:
\r
4746 ReloadPosition(0);
\r
4749 case IDM_SaveGame:
\r
4750 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4751 f = OpenFileDialog(hwnd, "a", defName,
\r
4752 appData.oldSaveStyle ? "gam" : "pgn",
\r
4754 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4756 SaveGame(f, 0, "");
\r
4760 case IDM_SavePosition:
\r
4761 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4762 f = OpenFileDialog(hwnd, "a", defName,
\r
4763 appData.oldSaveStyle ? "pos" : "fen",
\r
4765 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4767 SavePosition(f, 0, "");
\r
4771 case IDM_SaveDiagram:
\r
4772 defName = "diagram";
\r
4773 f = OpenFileDialog(hwnd, "wb", defName,
\r
4776 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4782 case IDM_CopyGame:
\r
4783 CopyGameToClipboard();
\r
4786 case IDM_PasteGame:
\r
4787 PasteGameFromClipboard();
\r
4790 case IDM_CopyGameListToClipboard:
\r
4791 CopyGameListToClipboard();
\r
4794 /* [AS] Autodetect FEN or PGN data */
\r
4795 case IDM_PasteAny:
\r
4796 PasteGameOrFENFromClipboard();
\r
4799 /* [AS] Move history */
\r
4800 case IDM_ShowMoveHistory:
\r
4801 if( MoveHistoryIsUp() ) {
\r
4802 MoveHistoryPopDown();
\r
4805 MoveHistoryPopUp();
\r
4809 /* [AS] Eval graph */
\r
4810 case IDM_ShowEvalGraph:
\r
4811 if( EvalGraphIsUp() ) {
\r
4812 EvalGraphPopDown();
\r
4816 SetFocus(hwndMain);
\r
4820 /* [AS] Engine output */
\r
4821 case IDM_ShowEngineOutput:
\r
4822 if( EngineOutputIsUp() ) {
\r
4823 EngineOutputPopDown();
\r
4826 EngineOutputPopUp();
\r
4830 /* [AS] User adjudication */
\r
4831 case IDM_UserAdjudication_White:
\r
4832 UserAdjudicationEvent( +1 );
\r
4835 case IDM_UserAdjudication_Black:
\r
4836 UserAdjudicationEvent( -1 );
\r
4839 case IDM_UserAdjudication_Draw:
\r
4840 UserAdjudicationEvent( 0 );
\r
4843 /* [AS] Game list options dialog */
\r
4844 case IDM_GameListOptions:
\r
4845 GameListOptions();
\r
4852 case IDM_CopyPosition:
\r
4853 CopyFENToClipboard();
\r
4856 case IDM_PastePosition:
\r
4857 PasteFENFromClipboard();
\r
4860 case IDM_MailMove:
\r
4864 case IDM_ReloadCMailMsg:
\r
4865 Reset(TRUE, TRUE);
\r
4866 ReloadCmailMsgEvent(FALSE);
\r
4869 case IDM_Minimize:
\r
4870 ShowWindow(hwnd, SW_MINIMIZE);
\r
4877 case IDM_MachineWhite:
\r
4878 MachineWhiteEvent();
\r
4880 * refresh the tags dialog only if it's visible
\r
4882 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4884 tags = PGNTags(&gameInfo);
\r
4885 TagsPopUp(tags, CmailMsg());
\r
4888 SAY("computer starts playing white");
\r
4891 case IDM_MachineBlack:
\r
4892 MachineBlackEvent();
\r
4894 * refresh the tags dialog only if it's visible
\r
4896 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4898 tags = PGNTags(&gameInfo);
\r
4899 TagsPopUp(tags, CmailMsg());
\r
4902 SAY("computer starts playing black");
\r
4905 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4906 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4909 case IDM_TwoMachines:
\r
4910 TwoMachinesEvent();
\r
4912 * refresh the tags dialog only if it's visible
\r
4914 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4916 tags = PGNTags(&gameInfo);
\r
4917 TagsPopUp(tags, CmailMsg());
\r
4920 SAY("computer starts playing both sides");
\r
4923 case IDM_AnalysisMode:
\r
4924 if (!first.analysisSupport) {
\r
4925 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4926 DisplayError(buf, 0);
\r
4928 SAY("analyzing current position");
\r
4929 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4930 if (appData.icsActive) {
\r
4931 if (gameMode != IcsObserving) {
\r
4932 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4933 DisplayError(buf, 0);
\r
4934 /* secure check */
\r
4935 if (appData.icsEngineAnalyze) {
\r
4936 if (appData.debugMode)
\r
4937 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4938 ExitAnalyzeMode();
\r
4944 /* if enable, user want disable icsEngineAnalyze */
\r
4945 if (appData.icsEngineAnalyze) {
\r
4946 ExitAnalyzeMode();
\r
4950 appData.icsEngineAnalyze = TRUE;
\r
4951 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4954 if (!appData.showThinking) ToggleShowThinking();
\r
4955 AnalyzeModeEvent();
\r
4959 case IDM_AnalyzeFile:
\r
4960 if (!first.analysisSupport) {
\r
4961 char buf[MSG_SIZ];
\r
4962 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4963 DisplayError(buf, 0);
\r
4965 if (!appData.showThinking) ToggleShowThinking();
\r
4966 AnalyzeFileEvent();
\r
4967 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4968 AnalysisPeriodicEvent(1);
\r
4972 case IDM_IcsClient:
\r
4976 case IDM_EditGame:
\r
4977 case IDM_EditGame2:
\r
4982 case IDM_EditPosition:
\r
4983 case IDM_EditPosition2:
\r
4984 EditPositionEvent();
\r
4985 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4988 case IDM_Training:
\r
4992 case IDM_ShowGameList:
\r
4993 ShowGameListProc();
\r
4996 case IDM_EditProgs1:
\r
4997 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5000 case IDM_LoadProg1:
\r
5001 LoadEnginePopUp(hwndMain, 0);
\r
5004 case IDM_LoadProg2:
\r
5005 LoadEnginePopUp(hwndMain, 1);
\r
5008 case IDM_EditServers:
\r
5009 EditTagsPopUp(icsNames, &icsNames);
\r
5012 case IDM_EditTags:
\r
5017 case IDM_EditBook:
\r
5021 case IDM_EditComment:
\r
5023 if (commentUp && editComment) {
\r
5026 EditCommentEvent();
\r
5046 case IDM_CallFlag:
\r
5066 case IDM_StopObserving:
\r
5067 StopObservingEvent();
\r
5070 case IDM_StopExamining:
\r
5071 StopExaminingEvent();
\r
5075 UploadGameEvent();
\r
5078 case IDM_TypeInMove:
\r
5079 TypeInEvent('\000');
\r
5082 case IDM_TypeInName:
\r
5083 PopUpNameDialog('\000');
\r
5086 case IDM_Backward:
\r
5088 SetFocus(hwndMain);
\r
5095 SetFocus(hwndMain);
\r
5100 SetFocus(hwndMain);
\r
5105 SetFocus(hwndMain);
\r
5108 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5109 case OPT_GameListPrev:
\r
5110 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5114 RevertEvent(FALSE);
\r
5117 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5118 RevertEvent(TRUE);
\r
5121 case IDM_TruncateGame:
\r
5122 TruncateGameEvent();
\r
5129 case IDM_RetractMove:
\r
5130 RetractMoveEvent();
\r
5133 case IDM_FlipView:
\r
5134 flipView = !flipView;
\r
5135 DrawPosition(FALSE, NULL);
\r
5138 case IDM_FlipClock:
\r
5139 flipClock = !flipClock;
\r
5140 DisplayBothClocks();
\r
5144 case IDM_MuteSounds:
\r
5145 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5146 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5147 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5150 case IDM_GeneralOptions:
\r
5151 GeneralOptionsPopup(hwnd);
\r
5152 DrawPosition(TRUE, NULL);
\r
5155 case IDM_BoardOptions:
\r
5156 BoardOptionsPopup(hwnd);
\r
5159 case IDM_EnginePlayOptions:
\r
5160 EnginePlayOptionsPopup(hwnd);
\r
5163 case IDM_Engine1Options:
\r
5164 EngineOptionsPopup(hwnd, &first);
\r
5167 case IDM_Engine2Options:
\r
5169 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5170 EngineOptionsPopup(hwnd, &second);
\r
5173 case IDM_OptionsUCI:
\r
5174 UciOptionsPopup(hwnd);
\r
5178 TourneyPopup(hwnd);
\r
5181 case IDM_IcsOptions:
\r
5182 IcsOptionsPopup(hwnd);
\r
5186 FontsOptionsPopup(hwnd);
\r
5190 SoundOptionsPopup(hwnd);
\r
5193 case IDM_CommPort:
\r
5194 CommPortOptionsPopup(hwnd);
\r
5197 case IDM_LoadOptions:
\r
5198 LoadOptionsPopup(hwnd);
\r
5201 case IDM_SaveOptions:
\r
5202 SaveOptionsPopup(hwnd);
\r
5205 case IDM_TimeControl:
\r
5206 TimeControlOptionsPopup(hwnd);
\r
5209 case IDM_SaveSettings:
\r
5210 SaveSettings(settingsFileName);
\r
5213 case IDM_SaveSettingsOnExit:
\r
5214 saveSettingsOnExit = !saveSettingsOnExit;
\r
5215 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5216 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5217 MF_CHECKED : MF_UNCHECKED));
\r
5228 case IDM_AboutGame:
\r
5233 appData.debugMode = !appData.debugMode;
\r
5234 if (appData.debugMode) {
\r
5235 char dir[MSG_SIZ];
\r
5236 GetCurrentDirectory(MSG_SIZ, dir);
\r
5237 SetCurrentDirectory(installDir);
\r
5238 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5239 SetCurrentDirectory(dir);
\r
5240 setbuf(debugFP, NULL);
\r
5247 case IDM_HELPCONTENTS:
\r
5248 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5249 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5250 MessageBox (GetFocus(),
\r
5251 _("Unable to activate help"),
\r
5252 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5256 case IDM_HELPSEARCH:
\r
5257 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5258 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5259 MessageBox (GetFocus(),
\r
5260 _("Unable to activate help"),
\r
5261 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5265 case IDM_HELPHELP:
\r
5266 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5267 MessageBox (GetFocus(),
\r
5268 _("Unable to activate help"),
\r
5269 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5274 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5276 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5277 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5278 FreeProcInstance(lpProc);
\r
5281 case IDM_DirectCommand1:
\r
5282 AskQuestionEvent(_("Direct Command"),
\r
5283 _("Send to chess program:"), "", "1");
\r
5285 case IDM_DirectCommand2:
\r
5286 AskQuestionEvent(_("Direct Command"),
\r
5287 _("Send to second chess program:"), "", "2");
\r
5290 case EP_WhitePawn:
\r
5291 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5292 fromX = fromY = -1;
\r
5295 case EP_WhiteKnight:
\r
5296 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5297 fromX = fromY = -1;
\r
5300 case EP_WhiteBishop:
\r
5301 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5302 fromX = fromY = -1;
\r
5305 case EP_WhiteRook:
\r
5306 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5307 fromX = fromY = -1;
\r
5310 case EP_WhiteQueen:
\r
5311 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5312 fromX = fromY = -1;
\r
5315 case EP_WhiteFerz:
\r
5316 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5317 fromX = fromY = -1;
\r
5320 case EP_WhiteWazir:
\r
5321 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5322 fromX = fromY = -1;
\r
5325 case EP_WhiteAlfil:
\r
5326 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5327 fromX = fromY = -1;
\r
5330 case EP_WhiteCannon:
\r
5331 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5332 fromX = fromY = -1;
\r
5335 case EP_WhiteCardinal:
\r
5336 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5337 fromX = fromY = -1;
\r
5340 case EP_WhiteMarshall:
\r
5341 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5342 fromX = fromY = -1;
\r
5345 case EP_WhiteKing:
\r
5346 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5347 fromX = fromY = -1;
\r
5350 case EP_BlackPawn:
\r
5351 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5352 fromX = fromY = -1;
\r
5355 case EP_BlackKnight:
\r
5356 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5357 fromX = fromY = -1;
\r
5360 case EP_BlackBishop:
\r
5361 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5362 fromX = fromY = -1;
\r
5365 case EP_BlackRook:
\r
5366 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5367 fromX = fromY = -1;
\r
5370 case EP_BlackQueen:
\r
5371 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5372 fromX = fromY = -1;
\r
5375 case EP_BlackFerz:
\r
5376 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5377 fromX = fromY = -1;
\r
5380 case EP_BlackWazir:
\r
5381 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5382 fromX = fromY = -1;
\r
5385 case EP_BlackAlfil:
\r
5386 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5387 fromX = fromY = -1;
\r
5390 case EP_BlackCannon:
\r
5391 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5392 fromX = fromY = -1;
\r
5395 case EP_BlackCardinal:
\r
5396 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5397 fromX = fromY = -1;
\r
5400 case EP_BlackMarshall:
\r
5401 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5402 fromX = fromY = -1;
\r
5405 case EP_BlackKing:
\r
5406 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5407 fromX = fromY = -1;
\r
5410 case EP_EmptySquare:
\r
5411 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5412 fromX = fromY = -1;
\r
5415 case EP_ClearBoard:
\r
5416 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5417 fromX = fromY = -1;
\r
5421 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5422 fromX = fromY = -1;
\r
5426 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5427 fromX = fromY = -1;
\r
5431 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5432 fromX = fromY = -1;
\r
5436 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5437 fromX = fromY = -1;
\r
5441 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5442 fromX = fromY = -1;
\r
5446 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5447 fromX = fromY = -1;
\r
5451 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5452 fromX = fromY = -1;
\r
5456 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5457 fromX = fromY = -1;
\r
5461 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5462 fromX = fromY = -1;
\r
5466 barbaric = 0; appData.language = "";
\r
5467 TranslateMenus(0);
\r
5468 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5469 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5470 lastChecked = wmId;
\r
5474 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5475 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5477 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5478 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5479 TranslateMenus(0);
\r
5480 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5481 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5482 lastChecked = wmId;
\r
5485 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5491 case CLOCK_TIMER_ID:
\r
5492 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5493 clockTimerEvent = 0;
\r
5494 DecrementClocks(); /* call into back end */
\r
5496 case LOAD_GAME_TIMER_ID:
\r
5497 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5498 loadGameTimerEvent = 0;
\r
5499 AutoPlayGameLoop(); /* call into back end */
\r
5501 case ANALYSIS_TIMER_ID:
\r
5502 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5503 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5504 AnalysisPeriodicEvent(0);
\r
5506 KillTimer(hwnd, analysisTimerEvent);
\r
5507 analysisTimerEvent = 0;
\r
5510 case DELAYED_TIMER_ID:
\r
5511 KillTimer(hwnd, delayedTimerEvent);
\r
5512 delayedTimerEvent = 0;
\r
5513 delayedTimerCallback();
\r
5518 case WM_USER_Input:
\r
5519 InputEvent(hwnd, message, wParam, lParam);
\r
5522 /* [AS] Also move "attached" child windows */
\r
5523 case WM_WINDOWPOSCHANGING:
\r
5525 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5526 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5528 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5529 /* Window is moving */
\r
5532 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5533 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5534 rcMain.right = wpMain.x + wpMain.width;
\r
5535 rcMain.top = wpMain.y;
\r
5536 rcMain.bottom = wpMain.y + wpMain.height;
\r
5538 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5539 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5540 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5541 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5542 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5543 wpMain.x = lpwp->x;
\r
5544 wpMain.y = lpwp->y;
\r
5549 /* [AS] Snapping */
\r
5550 case WM_ENTERSIZEMOVE:
\r
5551 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5552 if (hwnd == hwndMain) {
\r
5553 doingSizing = TRUE;
\r
5556 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5560 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5561 if (hwnd == hwndMain) {
\r
5562 lastSizing = wParam;
\r
5567 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5568 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5570 case WM_EXITSIZEMOVE:
\r
5571 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5572 if (hwnd == hwndMain) {
\r
5574 doingSizing = FALSE;
\r
5575 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5576 GetClientRect(hwnd, &client);
\r
5577 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5579 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5581 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5584 case WM_DESTROY: /* message: window being destroyed */
\r
5585 PostQuitMessage(0);
\r
5589 if (hwnd == hwndMain) {
\r
5594 default: /* Passes it on if unprocessed */
\r
5595 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5600 /*---------------------------------------------------------------------------*\
\r
5602 * Misc utility routines
\r
5604 \*---------------------------------------------------------------------------*/
\r
5607 * Decent random number generator, at least not as bad as Windows
\r
5608 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5610 unsigned int randstate;
\r
5615 randstate = randstate * 1664525 + 1013904223;
\r
5616 return (int) randstate & 0x7fffffff;
\r
5620 mysrandom(unsigned int seed)
\r
5627 * returns TRUE if user selects a different color, FALSE otherwise
\r
5631 ChangeColor(HWND hwnd, COLORREF *which)
\r
5633 static BOOL firstTime = TRUE;
\r
5634 static DWORD customColors[16];
\r
5636 COLORREF newcolor;
\r
5641 /* Make initial colors in use available as custom colors */
\r
5642 /* Should we put the compiled-in defaults here instead? */
\r
5644 customColors[i++] = lightSquareColor & 0xffffff;
\r
5645 customColors[i++] = darkSquareColor & 0xffffff;
\r
5646 customColors[i++] = whitePieceColor & 0xffffff;
\r
5647 customColors[i++] = blackPieceColor & 0xffffff;
\r
5648 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5649 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5651 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5652 customColors[i++] = textAttribs[ccl].color;
\r
5654 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5655 firstTime = FALSE;
\r
5658 cc.lStructSize = sizeof(cc);
\r
5659 cc.hwndOwner = hwnd;
\r
5660 cc.hInstance = NULL;
\r
5661 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5662 cc.lpCustColors = (LPDWORD) customColors;
\r
5663 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5665 if (!ChooseColor(&cc)) return FALSE;
\r
5667 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5668 if (newcolor == *which) return FALSE;
\r
5669 *which = newcolor;
\r
5673 InitDrawingColors();
\r
5674 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5679 MyLoadSound(MySound *ms)
\r
5685 if (ms->data && ms->flag) free(ms->data);
\r
5688 switch (ms->name[0]) {
\r
5694 /* System sound from Control Panel. Don't preload here. */
\r
5698 if (ms->name[1] == NULLCHAR) {
\r
5699 /* "!" alone = silence */
\r
5702 /* Builtin wave resource. Error if not found. */
\r
5703 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5704 if (h == NULL) break;
\r
5705 ms->data = (void *)LoadResource(hInst, h);
\r
5706 ms->flag = 0; // not maloced, so cannot be freed!
\r
5707 if (h == NULL) break;
\r
5712 /* .wav file. Error if not found. */
\r
5713 f = fopen(ms->name, "rb");
\r
5714 if (f == NULL) break;
\r
5715 if (fstat(fileno(f), &st) < 0) break;
\r
5716 ms->data = malloc(st.st_size);
\r
5718 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5724 char buf[MSG_SIZ];
\r
5725 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5726 DisplayError(buf, GetLastError());
\r
5732 MyPlaySound(MySound *ms)
\r
5734 BOOLEAN ok = FALSE;
\r
5736 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5737 switch (ms->name[0]) {
\r
5739 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5744 /* System sound from Control Panel (deprecated feature).
\r
5745 "$" alone or an unset sound name gets default beep (still in use). */
\r
5746 if (ms->name[1]) {
\r
5747 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5749 if (!ok) ok = MessageBeep(MB_OK);
\r
5752 /* Builtin wave resource, or "!" alone for silence */
\r
5753 if (ms->name[1]) {
\r
5754 if (ms->data == NULL) return FALSE;
\r
5755 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5761 /* .wav file. Error if not found. */
\r
5762 if (ms->data == NULL) return FALSE;
\r
5763 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5766 /* Don't print an error: this can happen innocently if the sound driver
\r
5767 is busy; for instance, if another instance of WinBoard is playing
\r
5768 a sound at about the same time. */
\r
5774 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5777 OPENFILENAME *ofn;
\r
5778 static UINT *number; /* gross that this is static */
\r
5780 switch (message) {
\r
5781 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5782 /* Center the dialog over the application window */
\r
5783 ofn = (OPENFILENAME *) lParam;
\r
5784 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5785 number = (UINT *) ofn->lCustData;
\r
5786 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5790 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5791 Translate(hDlg, 1536);
\r
5792 return FALSE; /* Allow for further processing */
\r
5795 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5796 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5798 return FALSE; /* Allow for further processing */
\r
5804 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5806 static UINT *number;
\r
5807 OPENFILENAME *ofname;
\r
5810 case WM_INITDIALOG:
\r
5811 Translate(hdlg, DLG_IndexNumber);
\r
5812 ofname = (OPENFILENAME *)lParam;
\r
5813 number = (UINT *)(ofname->lCustData);
\r
5816 ofnot = (OFNOTIFY *)lParam;
\r
5817 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5818 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5827 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5828 char *nameFilt, char *dlgTitle, UINT *number,
\r
5829 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5831 OPENFILENAME openFileName;
\r
5832 char buf1[MSG_SIZ];
\r
5835 if (fileName == NULL) fileName = buf1;
\r
5836 if (defName == NULL) {
\r
5837 safeStrCpy(fileName, "*.", 3 );
\r
5838 strcat(fileName, defExt);
\r
5840 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5842 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5843 if (number) *number = 0;
\r
5845 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5846 openFileName.hwndOwner = hwnd;
\r
5847 openFileName.hInstance = (HANDLE) hInst;
\r
5848 openFileName.lpstrFilter = nameFilt;
\r
5849 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5850 openFileName.nMaxCustFilter = 0L;
\r
5851 openFileName.nFilterIndex = 1L;
\r
5852 openFileName.lpstrFile = fileName;
\r
5853 openFileName.nMaxFile = MSG_SIZ;
\r
5854 openFileName.lpstrFileTitle = fileTitle;
\r
5855 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5856 openFileName.lpstrInitialDir = NULL;
\r
5857 openFileName.lpstrTitle = dlgTitle;
\r
5858 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5859 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5860 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5861 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5862 openFileName.nFileOffset = 0;
\r
5863 openFileName.nFileExtension = 0;
\r
5864 openFileName.lpstrDefExt = defExt;
\r
5865 openFileName.lCustData = (LONG) number;
\r
5866 openFileName.lpfnHook = oldDialog ?
\r
5867 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5868 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5870 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5871 GetOpenFileName(&openFileName)) {
\r
5872 /* open the file */
\r
5873 f = fopen(openFileName.lpstrFile, write);
\r
5875 MessageBox(hwnd, _("File open failed"), NULL,
\r
5876 MB_OK|MB_ICONEXCLAMATION);
\r
5880 int err = CommDlgExtendedError();
\r
5881 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5890 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5892 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5895 * Get the first pop-up menu in the menu template. This is the
\r
5896 * menu that TrackPopupMenu displays.
\r
5898 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5899 TranslateOneMenu(10, hmenuTrackPopup);
\r
5901 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5904 * TrackPopup uses screen coordinates, so convert the
\r
5905 * coordinates of the mouse click to screen coordinates.
\r
5907 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5909 /* Draw and track the floating pop-up menu. */
\r
5910 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5911 pt.x, pt.y, 0, hwnd, NULL);
\r
5913 /* Destroy the menu.*/
\r
5914 DestroyMenu(hmenu);
\r
5919 int sizeX, sizeY, newSizeX, newSizeY;
\r
5921 } ResizeEditPlusButtonsClosure;
\r
5924 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5926 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5930 if (hChild == cl->hText) return TRUE;
\r
5931 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5932 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5933 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5934 ScreenToClient(cl->hDlg, &pt);
\r
5935 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5936 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5940 /* Resize a dialog that has a (rich) edit field filling most of
\r
5941 the top, with a row of buttons below */
\r
5943 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5946 int newTextHeight, newTextWidth;
\r
5947 ResizeEditPlusButtonsClosure cl;
\r
5949 /*if (IsIconic(hDlg)) return;*/
\r
5950 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5952 cl.hdwp = BeginDeferWindowPos(8);
\r
5954 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5955 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5956 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5957 if (newTextHeight < 0) {
\r
5958 newSizeY += -newTextHeight;
\r
5959 newTextHeight = 0;
\r
5961 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5962 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5968 cl.newSizeX = newSizeX;
\r
5969 cl.newSizeY = newSizeY;
\r
5970 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5972 EndDeferWindowPos(cl.hdwp);
\r
5975 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5977 RECT rChild, rParent;
\r
5978 int wChild, hChild, wParent, hParent;
\r
5979 int wScreen, hScreen, xNew, yNew;
\r
5982 /* Get the Height and Width of the child window */
\r
5983 GetWindowRect (hwndChild, &rChild);
\r
5984 wChild = rChild.right - rChild.left;
\r
5985 hChild = rChild.bottom - rChild.top;
\r
5987 /* Get the Height and Width of the parent window */
\r
5988 GetWindowRect (hwndParent, &rParent);
\r
5989 wParent = rParent.right - rParent.left;
\r
5990 hParent = rParent.bottom - rParent.top;
\r
5992 /* Get the display limits */
\r
5993 hdc = GetDC (hwndChild);
\r
5994 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5995 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5996 ReleaseDC(hwndChild, hdc);
\r
5998 /* Calculate new X position, then adjust for screen */
\r
5999 xNew = rParent.left + ((wParent - wChild) /2);
\r
6002 } else if ((xNew+wChild) > wScreen) {
\r
6003 xNew = wScreen - wChild;
\r
6006 /* Calculate new Y position, then adjust for screen */
\r
6008 yNew = rParent.top + ((hParent - hChild) /2);
\r
6011 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6016 } else if ((yNew+hChild) > hScreen) {
\r
6017 yNew = hScreen - hChild;
\r
6020 /* Set it, and return */
\r
6021 return SetWindowPos (hwndChild, NULL,
\r
6022 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6025 /* Center one window over another */
\r
6026 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6028 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6031 /*---------------------------------------------------------------------------*\
\r
6033 * Startup Dialog functions
\r
6035 \*---------------------------------------------------------------------------*/
\r
6037 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6039 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6041 while (*cd != NULL) {
\r
6042 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6048 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6050 char buf1[MAX_ARG_LEN];
\r
6053 if (str[0] == '@') {
\r
6054 FILE* f = fopen(str + 1, "r");
\r
6056 DisplayFatalError(str + 1, errno, 2);
\r
6059 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6061 buf1[len] = NULLCHAR;
\r
6065 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6068 char buf[MSG_SIZ];
\r
6069 char *end = strchr(str, '\n');
\r
6070 if (end == NULL) return;
\r
6071 memcpy(buf, str, end - str);
\r
6072 buf[end - str] = NULLCHAR;
\r
6073 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6079 SetStartupDialogEnables(HWND hDlg)
\r
6081 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6082 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6083 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6084 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6085 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6086 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6087 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6088 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6089 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6090 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6091 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6092 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6093 IsDlgButtonChecked(hDlg, OPT_View));
\r
6097 QuoteForFilename(char *filename)
\r
6099 int dquote, space;
\r
6100 dquote = strchr(filename, '"') != NULL;
\r
6101 space = strchr(filename, ' ') != NULL;
\r
6102 if (dquote || space) {
\r
6114 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6116 char buf[MSG_SIZ];
\r
6119 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6120 q = QuoteForFilename(nthcp);
\r
6121 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6122 if (*nthdir != NULLCHAR) {
\r
6123 q = QuoteForFilename(nthdir);
\r
6124 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6126 if (*nthcp == NULLCHAR) {
\r
6127 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6128 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6129 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6130 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6135 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6137 char buf[MSG_SIZ];
\r
6141 switch (message) {
\r
6142 case WM_INITDIALOG:
\r
6143 /* Center the dialog */
\r
6144 CenterWindow (hDlg, GetDesktopWindow());
\r
6145 Translate(hDlg, DLG_Startup);
\r
6146 /* Initialize the dialog items */
\r
6147 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6148 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6149 firstChessProgramNames);
\r
6150 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6151 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6152 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6153 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6154 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6155 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6156 if (*appData.icsHelper != NULLCHAR) {
\r
6157 char *q = QuoteForFilename(appData.icsHelper);
\r
6158 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6160 if (*appData.icsHost == NULLCHAR) {
\r
6161 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6162 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6163 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6164 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6165 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6168 if (appData.icsActive) {
\r
6169 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6171 else if (appData.noChessProgram) {
\r
6172 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6175 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6178 SetStartupDialogEnables(hDlg);
\r
6182 switch (LOWORD(wParam)) {
\r
6184 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6185 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6186 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6188 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6189 ParseArgs(StringGet, &p);
\r
6190 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6191 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6193 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6194 ParseArgs(StringGet, &p);
\r
6195 SwapEngines(singleList); // ... and then make it 'second'
\r
6196 appData.noChessProgram = FALSE;
\r
6197 appData.icsActive = FALSE;
\r
6198 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6199 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6200 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6202 ParseArgs(StringGet, &p);
\r
6203 if (appData.zippyPlay) {
\r
6204 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6205 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6207 ParseArgs(StringGet, &p);
\r
6209 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6210 appData.noChessProgram = TRUE;
\r
6211 appData.icsActive = FALSE;
\r
6213 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6214 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6217 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6218 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6220 ParseArgs(StringGet, &p);
\r
6222 EndDialog(hDlg, TRUE);
\r
6229 case IDM_HELPCONTENTS:
\r
6230 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6231 MessageBox (GetFocus(),
\r
6232 _("Unable to activate help"),
\r
6233 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6238 SetStartupDialogEnables(hDlg);
\r
6246 /*---------------------------------------------------------------------------*\
\r
6248 * About box dialog functions
\r
6250 \*---------------------------------------------------------------------------*/
\r
6252 /* Process messages for "About" dialog box */
\r
6254 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6256 switch (message) {
\r
6257 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6258 /* Center the dialog over the application window */
\r
6259 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6260 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6261 Translate(hDlg, ABOUTBOX);
\r
6265 case WM_COMMAND: /* message: received a command */
\r
6266 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6267 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6268 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6276 /*---------------------------------------------------------------------------*\
\r
6278 * Comment Dialog functions
\r
6280 \*---------------------------------------------------------------------------*/
\r
6283 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6285 static HANDLE hwndText = NULL;
\r
6286 int len, newSizeX, newSizeY, flags;
\r
6287 static int sizeX, sizeY;
\r
6292 switch (message) {
\r
6293 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6294 /* Initialize the dialog items */
\r
6295 Translate(hDlg, DLG_EditComment);
\r
6296 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6297 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6298 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6299 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6300 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6301 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6302 SetWindowText(hDlg, commentTitle);
\r
6303 if (editComment) {
\r
6304 SetFocus(hwndText);
\r
6306 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6308 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6309 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6310 MAKELPARAM(FALSE, 0));
\r
6311 /* Size and position the dialog */
\r
6312 if (!commentDialog) {
\r
6313 commentDialog = hDlg;
\r
6314 flags = SWP_NOZORDER;
\r
6315 GetClientRect(hDlg, &rect);
\r
6316 sizeX = rect.right;
\r
6317 sizeY = rect.bottom;
\r
6318 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6319 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6320 WINDOWPLACEMENT wp;
\r
6321 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6322 wp.length = sizeof(WINDOWPLACEMENT);
\r
6324 wp.showCmd = SW_SHOW;
\r
6325 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6326 wp.rcNormalPosition.left = wpComment.x;
\r
6327 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6328 wp.rcNormalPosition.top = wpComment.y;
\r
6329 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6330 SetWindowPlacement(hDlg, &wp);
\r
6332 GetClientRect(hDlg, &rect);
\r
6333 newSizeX = rect.right;
\r
6334 newSizeY = rect.bottom;
\r
6335 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6336 newSizeX, newSizeY);
\r
6341 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6344 case WM_COMMAND: /* message: received a command */
\r
6345 switch (LOWORD(wParam)) {
\r
6347 if (editComment) {
\r
6349 /* Read changed options from the dialog box */
\r
6350 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6351 len = GetWindowTextLength(hwndText);
\r
6352 str = (char *) malloc(len + 1);
\r
6353 GetWindowText(hwndText, str, len + 1);
\r
6362 ReplaceComment(commentIndex, str);
\r
6369 case OPT_CancelComment:
\r
6373 case OPT_ClearComment:
\r
6374 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6377 case OPT_EditComment:
\r
6378 EditCommentEvent();
\r
6386 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6387 if( wParam == OPT_CommentText ) {
\r
6388 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6390 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6391 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6395 pt.x = LOWORD( lpMF->lParam );
\r
6396 pt.y = HIWORD( lpMF->lParam );
\r
6398 if(lpMF->msg == WM_CHAR) {
\r
6400 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6401 index = sel.cpMin;
\r
6403 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6405 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6406 len = GetWindowTextLength(hwndText);
\r
6407 str = (char *) malloc(len + 1);
\r
6408 GetWindowText(hwndText, str, len + 1);
\r
6409 ReplaceComment(commentIndex, str);
\r
6410 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6411 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6414 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6415 lpMF->msg = WM_USER;
\r
6423 newSizeX = LOWORD(lParam);
\r
6424 newSizeY = HIWORD(lParam);
\r
6425 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6430 case WM_GETMINMAXINFO:
\r
6431 /* Prevent resizing window too small */
\r
6432 mmi = (MINMAXINFO *) lParam;
\r
6433 mmi->ptMinTrackSize.x = 100;
\r
6434 mmi->ptMinTrackSize.y = 100;
\r
6441 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6446 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6448 if (str == NULL) str = "";
\r
6449 p = (char *) malloc(2 * strlen(str) + 2);
\r
6452 if (*str == '\n') *q++ = '\r';
\r
6456 if (commentText != NULL) free(commentText);
\r
6458 commentIndex = index;
\r
6459 commentTitle = title;
\r
6461 editComment = edit;
\r
6463 if (commentDialog) {
\r
6464 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6465 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6467 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6468 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6469 hwndMain, (DLGPROC)lpProc);
\r
6470 FreeProcInstance(lpProc);
\r
6476 /*---------------------------------------------------------------------------*\
\r
6478 * Type-in move dialog functions
\r
6480 \*---------------------------------------------------------------------------*/
\r
6483 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6485 char move[MSG_SIZ];
\r
6488 switch (message) {
\r
6489 case WM_INITDIALOG:
\r
6490 move[0] = (char) lParam;
\r
6491 move[1] = NULLCHAR;
\r
6492 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6493 Translate(hDlg, DLG_TypeInMove);
\r
6494 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6495 SetWindowText(hInput, move);
\r
6497 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6501 switch (LOWORD(wParam)) {
\r
6504 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6505 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6506 TypeInDoneEvent(move);
\r
6507 EndDialog(hDlg, TRUE);
\r
6510 EndDialog(hDlg, FALSE);
\r
6521 PopUpMoveDialog(char firstchar)
\r
6525 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6526 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6527 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6528 FreeProcInstance(lpProc);
\r
6531 /*---------------------------------------------------------------------------*\
\r
6533 * Type-in name dialog functions
\r
6535 \*---------------------------------------------------------------------------*/
\r
6538 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6540 char move[MSG_SIZ];
\r
6543 switch (message) {
\r
6544 case WM_INITDIALOG:
\r
6545 move[0] = (char) lParam;
\r
6546 move[1] = NULLCHAR;
\r
6547 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6548 Translate(hDlg, DLG_TypeInName);
\r
6549 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6550 SetWindowText(hInput, move);
\r
6552 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6556 switch (LOWORD(wParam)) {
\r
6558 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6559 appData.userName = strdup(move);
\r
6562 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6563 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6564 DisplayTitle(move);
\r
6568 EndDialog(hDlg, TRUE);
\r
6571 EndDialog(hDlg, FALSE);
\r
6582 PopUpNameDialog(char firstchar)
\r
6586 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6587 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6588 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6589 FreeProcInstance(lpProc);
\r
6592 /*---------------------------------------------------------------------------*\
\r
6596 \*---------------------------------------------------------------------------*/
\r
6598 /* Nonmodal error box */
\r
6599 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6600 WPARAM wParam, LPARAM lParam);
\r
6603 ErrorPopUp(char *title, char *content)
\r
6607 BOOLEAN modal = hwndMain == NULL;
\r
6625 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6626 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6629 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6631 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6632 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6633 hwndMain, (DLGPROC)lpProc);
\r
6634 FreeProcInstance(lpProc);
\r
6641 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6642 if (errorDialog == NULL) return;
\r
6643 DestroyWindow(errorDialog);
\r
6644 errorDialog = NULL;
\r
6645 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6649 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6654 switch (message) {
\r
6655 case WM_INITDIALOG:
\r
6656 GetWindowRect(hDlg, &rChild);
\r
6659 SetWindowPos(hDlg, NULL, rChild.left,
\r
6660 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6661 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6665 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6666 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6667 and it doesn't work when you resize the dialog.
\r
6668 For now, just give it a default position.
\r
6670 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6671 Translate(hDlg, DLG_Error);
\r
6673 errorDialog = hDlg;
\r
6674 SetWindowText(hDlg, errorTitle);
\r
6675 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6676 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6680 switch (LOWORD(wParam)) {
\r
6683 if (errorDialog == hDlg) errorDialog = NULL;
\r
6684 DestroyWindow(hDlg);
\r
6696 HWND gothicDialog = NULL;
\r
6699 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6703 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6705 switch (message) {
\r
6706 case WM_INITDIALOG:
\r
6707 GetWindowRect(hDlg, &rChild);
\r
6709 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6713 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6714 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6715 and it doesn't work when you resize the dialog.
\r
6716 For now, just give it a default position.
\r
6718 gothicDialog = hDlg;
\r
6719 SetWindowText(hDlg, errorTitle);
\r
6720 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6721 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6725 switch (LOWORD(wParam)) {
\r
6728 if (errorDialog == hDlg) errorDialog = NULL;
\r
6729 DestroyWindow(hDlg);
\r
6741 GothicPopUp(char *title, VariantClass variant)
\r
6744 static char *lastTitle;
\r
6746 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6747 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6749 if(lastTitle != title && gothicDialog != NULL) {
\r
6750 DestroyWindow(gothicDialog);
\r
6751 gothicDialog = NULL;
\r
6753 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6754 title = lastTitle;
\r
6755 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6756 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6757 hwndMain, (DLGPROC)lpProc);
\r
6758 FreeProcInstance(lpProc);
\r
6763 /*---------------------------------------------------------------------------*\
\r
6765 * Ics Interaction console functions
\r
6767 \*---------------------------------------------------------------------------*/
\r
6769 #define HISTORY_SIZE 64
\r
6770 static char *history[HISTORY_SIZE];
\r
6771 int histIn = 0, histP = 0;
\r
6774 SaveInHistory(char *cmd)
\r
6776 if (history[histIn] != NULL) {
\r
6777 free(history[histIn]);
\r
6778 history[histIn] = NULL;
\r
6780 if (*cmd == NULLCHAR) return;
\r
6781 history[histIn] = StrSave(cmd);
\r
6782 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6783 if (history[histIn] != NULL) {
\r
6784 free(history[histIn]);
\r
6785 history[histIn] = NULL;
\r
6791 PrevInHistory(char *cmd)
\r
6794 if (histP == histIn) {
\r
6795 if (history[histIn] != NULL) free(history[histIn]);
\r
6796 history[histIn] = StrSave(cmd);
\r
6798 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6799 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6801 return history[histP];
\r
6807 if (histP == histIn) return NULL;
\r
6808 histP = (histP + 1) % HISTORY_SIZE;
\r
6809 return history[histP];
\r
6813 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6817 hmenu = LoadMenu(hInst, "TextMenu");
\r
6818 h = GetSubMenu(hmenu, 0);
\r
6820 if (strcmp(e->item, "-") == 0) {
\r
6821 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6822 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6823 int flags = MF_STRING, j = 0;
\r
6824 if (e->item[0] == '|') {
\r
6825 flags |= MF_MENUBARBREAK;
\r
6828 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6829 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6837 WNDPROC consoleTextWindowProc;
\r
6840 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6842 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6843 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6847 SetWindowText(hInput, command);
\r
6849 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6851 sel.cpMin = 999999;
\r
6852 sel.cpMax = 999999;
\r
6853 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6858 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6859 if (sel.cpMin == sel.cpMax) {
\r
6860 /* Expand to surrounding word */
\r
6863 tr.chrg.cpMax = sel.cpMin;
\r
6864 tr.chrg.cpMin = --sel.cpMin;
\r
6865 if (sel.cpMin < 0) break;
\r
6866 tr.lpstrText = name;
\r
6867 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6868 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6872 tr.chrg.cpMin = sel.cpMax;
\r
6873 tr.chrg.cpMax = ++sel.cpMax;
\r
6874 tr.lpstrText = name;
\r
6875 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6876 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6879 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6880 MessageBeep(MB_ICONEXCLAMATION);
\r
6884 tr.lpstrText = name;
\r
6885 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6887 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6888 MessageBeep(MB_ICONEXCLAMATION);
\r
6891 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6894 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6895 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6896 SetWindowText(hInput, buf);
\r
6897 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6899 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6900 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6901 SetWindowText(hInput, buf);
\r
6902 sel.cpMin = 999999;
\r
6903 sel.cpMax = 999999;
\r
6904 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6910 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6915 switch (message) {
\r
6917 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6918 if(wParam=='R') return 0;
\r
6921 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6924 sel.cpMin = 999999;
\r
6925 sel.cpMax = 999999;
\r
6926 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6927 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6932 if(wParam != '\022') {
\r
6933 if (wParam == '\t') {
\r
6934 if (GetKeyState(VK_SHIFT) < 0) {
\r
6936 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6937 if (buttonDesc[0].hwnd) {
\r
6938 SetFocus(buttonDesc[0].hwnd);
\r
6940 SetFocus(hwndMain);
\r
6944 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6947 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6948 JAWS_DELETE( SetFocus(hInput); )
\r
6949 SendMessage(hInput, message, wParam, lParam);
\r
6952 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6954 case WM_RBUTTONDOWN:
\r
6955 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6956 /* Move selection here if it was empty */
\r
6958 pt.x = LOWORD(lParam);
\r
6959 pt.y = HIWORD(lParam);
\r
6960 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6961 if (sel.cpMin == sel.cpMax) {
\r
6962 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6963 sel.cpMax = sel.cpMin;
\r
6964 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6966 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6967 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6969 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6970 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6971 if (sel.cpMin == sel.cpMax) {
\r
6972 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6973 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6975 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6976 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6978 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6979 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6980 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6981 MenuPopup(hwnd, pt, hmenu, -1);
\r
6985 case WM_RBUTTONUP:
\r
6986 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6987 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6988 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6992 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6994 return SendMessage(hInput, message, wParam, lParam);
\r
6995 case WM_MBUTTONDOWN:
\r
6996 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6998 switch (LOWORD(wParam)) {
\r
6999 case IDM_QuickPaste:
\r
7001 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7002 if (sel.cpMin == sel.cpMax) {
\r
7003 MessageBeep(MB_ICONEXCLAMATION);
\r
7006 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7007 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7008 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7013 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7016 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7019 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7023 int i = LOWORD(wParam) - IDM_CommandX;
\r
7024 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7025 icsTextMenuEntry[i].command != NULL) {
\r
7026 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7027 icsTextMenuEntry[i].getname,
\r
7028 icsTextMenuEntry[i].immediate);
\r
7036 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7039 WNDPROC consoleInputWindowProc;
\r
7042 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7044 char buf[MSG_SIZ];
\r
7046 static BOOL sendNextChar = FALSE;
\r
7047 static BOOL quoteNextChar = FALSE;
\r
7048 InputSource *is = consoleInputSource;
\r
7052 switch (message) {
\r
7054 if (!appData.localLineEditing || sendNextChar) {
\r
7055 is->buf[0] = (CHAR) wParam;
\r
7057 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7058 sendNextChar = FALSE;
\r
7061 if (quoteNextChar) {
\r
7062 buf[0] = (char) wParam;
\r
7063 buf[1] = NULLCHAR;
\r
7064 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7065 quoteNextChar = FALSE;
\r
7069 case '\r': /* Enter key */
\r
7070 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7071 if (consoleEcho) SaveInHistory(is->buf);
\r
7072 is->buf[is->count++] = '\n';
\r
7073 is->buf[is->count] = NULLCHAR;
\r
7074 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7075 if (consoleEcho) {
\r
7076 ConsoleOutput(is->buf, is->count, TRUE);
\r
7077 } else if (appData.localLineEditing) {
\r
7078 ConsoleOutput("\n", 1, TRUE);
\r
7081 case '\033': /* Escape key */
\r
7082 SetWindowText(hwnd, "");
\r
7083 cf.cbSize = sizeof(CHARFORMAT);
\r
7084 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7085 if (consoleEcho) {
\r
7086 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7088 cf.crTextColor = COLOR_ECHOOFF;
\r
7090 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7091 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7093 case '\t': /* Tab key */
\r
7094 if (GetKeyState(VK_SHIFT) < 0) {
\r
7096 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7099 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7100 if (buttonDesc[0].hwnd) {
\r
7101 SetFocus(buttonDesc[0].hwnd);
\r
7103 SetFocus(hwndMain);
\r
7107 case '\023': /* Ctrl+S */
\r
7108 sendNextChar = TRUE;
\r
7110 case '\021': /* Ctrl+Q */
\r
7111 quoteNextChar = TRUE;
\r
7121 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7122 p = PrevInHistory(buf);
\r
7124 SetWindowText(hwnd, p);
\r
7125 sel.cpMin = 999999;
\r
7126 sel.cpMax = 999999;
\r
7127 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7132 p = NextInHistory();
\r
7134 SetWindowText(hwnd, p);
\r
7135 sel.cpMin = 999999;
\r
7136 sel.cpMax = 999999;
\r
7137 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7143 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7147 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7151 case WM_MBUTTONDOWN:
\r
7152 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7153 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7155 case WM_RBUTTONUP:
\r
7156 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7157 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7158 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7162 hmenu = LoadMenu(hInst, "InputMenu");
\r
7163 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7164 if (sel.cpMin == sel.cpMax) {
\r
7165 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7166 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7168 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7169 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7171 pt.x = LOWORD(lParam);
\r
7172 pt.y = HIWORD(lParam);
\r
7173 MenuPopup(hwnd, pt, hmenu, -1);
\r
7177 switch (LOWORD(wParam)) {
\r
7179 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7181 case IDM_SelectAll:
\r
7183 sel.cpMax = -1; /*999999?*/
\r
7184 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7187 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7190 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7193 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7198 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7201 #define CO_MAX 100000
\r
7202 #define CO_TRIM 1000
\r
7205 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7207 static SnapData sd;
\r
7208 HWND hText, hInput;
\r
7210 static int sizeX, sizeY;
\r
7211 int newSizeX, newSizeY;
\r
7215 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7216 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7218 switch (message) {
\r
7220 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7222 ENLINK *pLink = (ENLINK*)lParam;
\r
7223 if (pLink->msg == WM_LBUTTONUP)
\r
7227 tr.chrg = pLink->chrg;
\r
7228 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7229 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7230 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7231 free(tr.lpstrText);
\r
7235 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7236 hwndConsole = hDlg;
\r
7238 consoleTextWindowProc = (WNDPROC)
\r
7239 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7240 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7241 consoleInputWindowProc = (WNDPROC)
\r
7242 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7243 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7244 Colorize(ColorNormal, TRUE);
\r
7245 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7246 ChangedConsoleFont();
\r
7247 GetClientRect(hDlg, &rect);
\r
7248 sizeX = rect.right;
\r
7249 sizeY = rect.bottom;
\r
7250 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7251 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7252 WINDOWPLACEMENT wp;
\r
7253 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7254 wp.length = sizeof(WINDOWPLACEMENT);
\r
7256 wp.showCmd = SW_SHOW;
\r
7257 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7258 wp.rcNormalPosition.left = wpConsole.x;
\r
7259 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7260 wp.rcNormalPosition.top = wpConsole.y;
\r
7261 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7262 SetWindowPlacement(hDlg, &wp);
\r
7265 // [HGM] Chessknight's change 2004-07-13
\r
7266 else { /* Determine Defaults */
\r
7267 WINDOWPLACEMENT wp;
\r
7268 wpConsole.x = wpMain.width + 1;
\r
7269 wpConsole.y = wpMain.y;
\r
7270 wpConsole.width = screenWidth - wpMain.width;
\r
7271 wpConsole.height = wpMain.height;
\r
7272 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7273 wp.length = sizeof(WINDOWPLACEMENT);
\r
7275 wp.showCmd = SW_SHOW;
\r
7276 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7277 wp.rcNormalPosition.left = wpConsole.x;
\r
7278 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7279 wp.rcNormalPosition.top = wpConsole.y;
\r
7280 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7281 SetWindowPlacement(hDlg, &wp);
\r
7284 // Allow hText to highlight URLs and send notifications on them
\r
7285 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7286 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7287 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7288 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7302 if (IsIconic(hDlg)) break;
\r
7303 newSizeX = LOWORD(lParam);
\r
7304 newSizeY = HIWORD(lParam);
\r
7305 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7306 RECT rectText, rectInput;
\r
7308 int newTextHeight, newTextWidth;
\r
7309 GetWindowRect(hText, &rectText);
\r
7310 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7311 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7312 if (newTextHeight < 0) {
\r
7313 newSizeY += -newTextHeight;
\r
7314 newTextHeight = 0;
\r
7316 SetWindowPos(hText, NULL, 0, 0,
\r
7317 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7318 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7319 pt.x = rectInput.left;
\r
7320 pt.y = rectInput.top + newSizeY - sizeY;
\r
7321 ScreenToClient(hDlg, &pt);
\r
7322 SetWindowPos(hInput, NULL,
\r
7323 pt.x, pt.y, /* needs client coords */
\r
7324 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7325 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7331 case WM_GETMINMAXINFO:
\r
7332 /* Prevent resizing window too small */
\r
7333 mmi = (MINMAXINFO *) lParam;
\r
7334 mmi->ptMinTrackSize.x = 100;
\r
7335 mmi->ptMinTrackSize.y = 100;
\r
7338 /* [AS] Snapping */
\r
7339 case WM_ENTERSIZEMOVE:
\r
7340 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7343 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7346 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7348 case WM_EXITSIZEMOVE:
\r
7349 UpdateICSWidth(hText);
\r
7350 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7353 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7361 if (hwndConsole) return;
\r
7362 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7363 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7368 ConsoleOutput(char* data, int length, int forceVisible)
\r
7373 char buf[CO_MAX+1];
\r
7376 static int delayLF = 0;
\r
7377 CHARRANGE savesel, sel;
\r
7379 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7387 while (length--) {
\r
7395 } else if (*p == '\007') {
\r
7396 MyPlaySound(&sounds[(int)SoundBell]);
\r
7403 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7404 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7405 /* Save current selection */
\r
7406 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7407 exlen = GetWindowTextLength(hText);
\r
7408 /* Find out whether current end of text is visible */
\r
7409 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7410 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7411 /* Trim existing text if it's too long */
\r
7412 if (exlen + (q - buf) > CO_MAX) {
\r
7413 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7416 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7417 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7419 savesel.cpMin -= trim;
\r
7420 savesel.cpMax -= trim;
\r
7421 if (exlen < 0) exlen = 0;
\r
7422 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7423 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7425 /* Append the new text */
\r
7426 sel.cpMin = exlen;
\r
7427 sel.cpMax = exlen;
\r
7428 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7429 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7430 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7431 if (forceVisible || exlen == 0 ||
\r
7432 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7433 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7434 /* Scroll to make new end of text visible if old end of text
\r
7435 was visible or new text is an echo of user typein */
\r
7436 sel.cpMin = 9999999;
\r
7437 sel.cpMax = 9999999;
\r
7438 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7439 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7440 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7441 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7443 if (savesel.cpMax == exlen || forceVisible) {
\r
7444 /* Move insert point to new end of text if it was at the old
\r
7445 end of text or if the new text is an echo of user typein */
\r
7446 sel.cpMin = 9999999;
\r
7447 sel.cpMax = 9999999;
\r
7448 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7450 /* Restore previous selection */
\r
7451 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7453 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7460 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7464 COLORREF oldFg, oldBg;
\r
7468 if(copyNumber > 1)
\r
7469 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7471 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7472 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7473 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7476 rect.right = x + squareSize;
\r
7478 rect.bottom = y + squareSize;
\r
7481 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7482 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7483 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7484 &rect, str, strlen(str), NULL);
\r
7486 (void) SetTextColor(hdc, oldFg);
\r
7487 (void) SetBkColor(hdc, oldBg);
\r
7488 (void) SelectObject(hdc, oldFont);
\r
7492 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7493 RECT *rect, char *color, char *flagFell)
\r
7497 COLORREF oldFg, oldBg;
\r
7500 if (appData.clockMode) {
\r
7502 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7504 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7511 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7512 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7514 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7515 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7517 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7521 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7522 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7523 rect, str, strlen(str), NULL);
\r
7524 if(logoHeight > 0 && appData.clockMode) {
\r
7526 str += strlen(color)+2;
\r
7527 r.top = rect->top + logoHeight/2;
\r
7528 r.left = rect->left;
\r
7529 r.right = rect->right;
\r
7530 r.bottom = rect->bottom;
\r
7531 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7532 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7533 &r, str, strlen(str), NULL);
\r
7535 (void) SetTextColor(hdc, oldFg);
\r
7536 (void) SetBkColor(hdc, oldBg);
\r
7537 (void) SelectObject(hdc, oldFont);
\r
7542 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7548 if( count <= 0 ) {
\r
7549 if (appData.debugMode) {
\r
7550 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7553 return ERROR_INVALID_USER_BUFFER;
\r
7556 ResetEvent(ovl->hEvent);
\r
7557 ovl->Offset = ovl->OffsetHigh = 0;
\r
7558 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7562 err = GetLastError();
\r
7563 if (err == ERROR_IO_PENDING) {
\r
7564 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7568 err = GetLastError();
\r
7575 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7580 ResetEvent(ovl->hEvent);
\r
7581 ovl->Offset = ovl->OffsetHigh = 0;
\r
7582 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7586 err = GetLastError();
\r
7587 if (err == ERROR_IO_PENDING) {
\r
7588 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7592 err = GetLastError();
\r
7598 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7599 void CheckForInputBufferFull( InputSource * is )
\r
7601 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7602 /* Look for end of line */
\r
7603 char * p = is->buf;
\r
7605 while( p < is->next && *p != '\n' ) {
\r
7609 if( p >= is->next ) {
\r
7610 if (appData.debugMode) {
\r
7611 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7614 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7615 is->count = (DWORD) -1;
\r
7616 is->next = is->buf;
\r
7622 InputThread(LPVOID arg)
\r
7627 is = (InputSource *) arg;
\r
7628 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7629 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7630 while (is->hThread != NULL) {
\r
7631 is->error = DoReadFile(is->hFile, is->next,
\r
7632 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7633 &is->count, &ovl);
\r
7634 if (is->error == NO_ERROR) {
\r
7635 is->next += is->count;
\r
7637 if (is->error == ERROR_BROKEN_PIPE) {
\r
7638 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7641 is->count = (DWORD) -1;
\r
7642 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7647 CheckForInputBufferFull( is );
\r
7649 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7651 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7653 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7656 CloseHandle(ovl.hEvent);
\r
7657 CloseHandle(is->hFile);
\r
7659 if (appData.debugMode) {
\r
7660 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7667 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7669 NonOvlInputThread(LPVOID arg)
\r
7676 is = (InputSource *) arg;
\r
7677 while (is->hThread != NULL) {
\r
7678 is->error = ReadFile(is->hFile, is->next,
\r
7679 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7680 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7681 if (is->error == NO_ERROR) {
\r
7682 /* Change CRLF to LF */
\r
7683 if (is->next > is->buf) {
\r
7685 i = is->count + 1;
\r
7693 if (prev == '\r' && *p == '\n') {
\r
7705 if (is->error == ERROR_BROKEN_PIPE) {
\r
7706 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7709 is->count = (DWORD) -1;
\r
7713 CheckForInputBufferFull( is );
\r
7715 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7717 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7719 if (is->count < 0) break; /* Quit on error */
\r
7721 CloseHandle(is->hFile);
\r
7726 SocketInputThread(LPVOID arg)
\r
7730 is = (InputSource *) arg;
\r
7731 while (is->hThread != NULL) {
\r
7732 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7733 if ((int)is->count == SOCKET_ERROR) {
\r
7734 is->count = (DWORD) -1;
\r
7735 is->error = WSAGetLastError();
\r
7737 is->error = NO_ERROR;
\r
7738 is->next += is->count;
\r
7739 if (is->count == 0 && is->second == is) {
\r
7740 /* End of file on stderr; quit with no message */
\r
7744 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7746 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7748 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7754 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7758 is = (InputSource *) lParam;
\r
7759 if (is->lineByLine) {
\r
7760 /* Feed in lines one by one */
\r
7761 char *p = is->buf;
\r
7763 while (q < is->next) {
\r
7764 if (*q++ == '\n') {
\r
7765 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7770 /* Move any partial line to the start of the buffer */
\r
7772 while (p < is->next) {
\r
7777 if (is->error != NO_ERROR || is->count == 0) {
\r
7778 /* Notify backend of the error. Note: If there was a partial
\r
7779 line at the end, it is not flushed through. */
\r
7780 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7783 /* Feed in the whole chunk of input at once */
\r
7784 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7785 is->next = is->buf;
\r
7789 /*---------------------------------------------------------------------------*\
\r
7791 * Menu enables. Used when setting various modes.
\r
7793 \*---------------------------------------------------------------------------*/
\r
7801 GreyRevert(Boolean grey)
\r
7802 { // [HGM] vari: for retracting variations in local mode
\r
7803 HMENU hmenu = GetMenu(hwndMain);
\r
7804 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7805 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7809 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7811 while (enab->item > 0) {
\r
7812 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7817 Enables gnuEnables[] = {
\r
7818 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7832 // Needed to switch from ncp to GNU mode on Engine Load
\r
7833 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7834 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7835 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7836 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7837 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7838 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7840 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7841 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7842 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7843 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7844 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7846 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7850 Enables icsEnables[] = {
\r
7851 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7875 Enables zippyEnables[] = {
\r
7876 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7884 Enables ncpEnables[] = {
\r
7885 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7895 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7910 Enables trainingOnEnables[] = {
\r
7911 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7923 Enables trainingOffEnables[] = {
\r
7924 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7936 /* These modify either ncpEnables or gnuEnables */
\r
7937 Enables cmailEnables[] = {
\r
7938 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7940 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7941 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7943 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7948 Enables machineThinkingEnables[] = {
\r
7949 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7962 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7968 Enables userThinkingEnables[] = {
\r
7969 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7970 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7971 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7972 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7973 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7975 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7976 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7977 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7978 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7981 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7982 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7983 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7984 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7988 /*---------------------------------------------------------------------------*\
\r
7990 * Front-end interface functions exported by XBoard.
\r
7991 * Functions appear in same order as prototypes in frontend.h.
\r
7993 \*---------------------------------------------------------------------------*/
\r
7995 CheckMark(UINT item, int state)
\r
7997 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8003 static UINT prevChecked = 0;
\r
8004 static int prevPausing = 0;
\r
8007 if (pausing != prevPausing) {
\r
8008 prevPausing = pausing;
\r
8009 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8010 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8011 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8014 switch (gameMode) {
\r
8015 case BeginningOfGame:
\r
8016 if (appData.icsActive)
\r
8017 nowChecked = IDM_IcsClient;
\r
8018 else if (appData.noChessProgram)
\r
8019 nowChecked = IDM_EditGame;
\r
8021 nowChecked = IDM_MachineBlack;
\r
8023 case MachinePlaysBlack:
\r
8024 nowChecked = IDM_MachineBlack;
\r
8026 case MachinePlaysWhite:
\r
8027 nowChecked = IDM_MachineWhite;
\r
8029 case TwoMachinesPlay:
\r
8030 nowChecked = IDM_TwoMachines;
\r
8033 nowChecked = IDM_AnalysisMode;
\r
8036 nowChecked = IDM_AnalyzeFile;
\r
8039 nowChecked = IDM_EditGame;
\r
8041 case PlayFromGameFile:
\r
8042 nowChecked = IDM_LoadGame;
\r
8044 case EditPosition:
\r
8045 nowChecked = IDM_EditPosition;
\r
8048 nowChecked = IDM_Training;
\r
8050 case IcsPlayingWhite:
\r
8051 case IcsPlayingBlack:
\r
8052 case IcsObserving:
\r
8054 nowChecked = IDM_IcsClient;
\r
8061 CheckMark(prevChecked, MF_UNCHECKED);
\r
8062 CheckMark(nowChecked, MF_CHECKED);
\r
8063 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8065 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8066 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8067 MF_BYCOMMAND|MF_ENABLED);
\r
8069 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8070 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8073 prevChecked = nowChecked;
\r
8075 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8076 if (appData.icsActive) {
\r
8077 if (appData.icsEngineAnalyze) {
\r
8078 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8080 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8083 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8089 HMENU hmenu = GetMenu(hwndMain);
\r
8090 SetMenuEnables(hmenu, icsEnables);
\r
8091 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8092 MF_BYCOMMAND|MF_ENABLED);
\r
8094 if (appData.zippyPlay) {
\r
8095 SetMenuEnables(hmenu, zippyEnables);
\r
8096 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8097 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8098 MF_BYCOMMAND|MF_ENABLED);
\r
8106 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8112 HMENU hmenu = GetMenu(hwndMain);
\r
8113 SetMenuEnables(hmenu, ncpEnables);
\r
8114 DrawMenuBar(hwndMain);
\r
8120 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8124 SetTrainingModeOn()
\r
8127 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8128 for (i = 0; i < N_BUTTONS; i++) {
\r
8129 if (buttonDesc[i].hwnd != NULL)
\r
8130 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8135 VOID SetTrainingModeOff()
\r
8138 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8139 for (i = 0; i < N_BUTTONS; i++) {
\r
8140 if (buttonDesc[i].hwnd != NULL)
\r
8141 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8147 SetUserThinkingEnables()
\r
8149 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8153 SetMachineThinkingEnables()
\r
8155 HMENU hMenu = GetMenu(hwndMain);
\r
8156 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8158 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8160 if (gameMode == MachinePlaysBlack) {
\r
8161 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8162 } else if (gameMode == MachinePlaysWhite) {
\r
8163 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8164 } else if (gameMode == TwoMachinesPlay) {
\r
8165 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8171 DisplayTitle(char *str)
\r
8173 char title[MSG_SIZ], *host;
\r
8174 if (str[0] != NULLCHAR) {
\r
8175 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8176 } else if (appData.icsActive) {
\r
8177 if (appData.icsCommPort[0] != NULLCHAR)
\r
8180 host = appData.icsHost;
\r
8181 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8182 } else if (appData.noChessProgram) {
\r
8183 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8185 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8186 strcat(title, ": ");
\r
8187 strcat(title, first.tidy);
\r
8189 SetWindowText(hwndMain, title);
\r
8194 DisplayMessage(char *str1, char *str2)
\r
8198 int remain = MESSAGE_TEXT_MAX - 1;
\r
8201 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8202 messageText[0] = NULLCHAR;
\r
8204 len = strlen(str1);
\r
8205 if (len > remain) len = remain;
\r
8206 strncpy(messageText, str1, len);
\r
8207 messageText[len] = NULLCHAR;
\r
8210 if (*str2 && remain >= 2) {
\r
8212 strcat(messageText, " ");
\r
8215 len = strlen(str2);
\r
8216 if (len > remain) len = remain;
\r
8217 strncat(messageText, str2, len);
\r
8219 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8220 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8222 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8226 hdc = GetDC(hwndMain);
\r
8227 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8228 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8229 &messageRect, messageText, strlen(messageText), NULL);
\r
8230 (void) SelectObject(hdc, oldFont);
\r
8231 (void) ReleaseDC(hwndMain, hdc);
\r
8235 DisplayError(char *str, int error)
\r
8237 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8241 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8243 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8244 NULL, error, LANG_NEUTRAL,
\r
8245 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8247 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8249 ErrorMap *em = errmap;
\r
8250 while (em->err != 0 && em->err != error) em++;
\r
8251 if (em->err != 0) {
\r
8252 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8254 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8259 ErrorPopUp(_("Error"), buf);
\r
8264 DisplayMoveError(char *str)
\r
8266 fromX = fromY = -1;
\r
8267 ClearHighlights();
\r
8268 DrawPosition(FALSE, NULL);
\r
8269 if (appData.popupMoveErrors) {
\r
8270 ErrorPopUp(_("Error"), str);
\r
8272 DisplayMessage(str, "");
\r
8273 moveErrorMessageUp = TRUE;
\r
8278 DisplayFatalError(char *str, int error, int exitStatus)
\r
8280 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8282 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8285 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8286 NULL, error, LANG_NEUTRAL,
\r
8287 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8289 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8291 ErrorMap *em = errmap;
\r
8292 while (em->err != 0 && em->err != error) em++;
\r
8293 if (em->err != 0) {
\r
8294 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8296 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8301 if (appData.debugMode) {
\r
8302 fprintf(debugFP, "%s: %s\n", label, str);
\r
8304 if (appData.popupExitMessage) {
\r
8305 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8306 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8308 ExitEvent(exitStatus);
\r
8313 DisplayInformation(char *str)
\r
8315 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8320 DisplayNote(char *str)
\r
8322 ErrorPopUp(_("Note"), str);
\r
8327 char *title, *question, *replyPrefix;
\r
8332 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8334 static QuestionParams *qp;
\r
8335 char reply[MSG_SIZ];
\r
8338 switch (message) {
\r
8339 case WM_INITDIALOG:
\r
8340 qp = (QuestionParams *) lParam;
\r
8341 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8342 Translate(hDlg, DLG_Question);
\r
8343 SetWindowText(hDlg, qp->title);
\r
8344 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8345 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8349 switch (LOWORD(wParam)) {
\r
8351 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8352 if (*reply) strcat(reply, " ");
\r
8353 len = strlen(reply);
\r
8354 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8355 strcat(reply, "\n");
\r
8356 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8357 EndDialog(hDlg, TRUE);
\r
8358 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8361 EndDialog(hDlg, FALSE);
\r
8372 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8374 QuestionParams qp;
\r
8378 qp.question = question;
\r
8379 qp.replyPrefix = replyPrefix;
\r
8381 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8382 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8383 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8384 FreeProcInstance(lpProc);
\r
8387 /* [AS] Pick FRC position */
\r
8388 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8390 static int * lpIndexFRC;
\r
8396 case WM_INITDIALOG:
\r
8397 lpIndexFRC = (int *) lParam;
\r
8399 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8400 Translate(hDlg, DLG_NewGameFRC);
\r
8402 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8403 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8404 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8405 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8410 switch( LOWORD(wParam) ) {
\r
8412 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8413 EndDialog( hDlg, 0 );
\r
8414 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8417 EndDialog( hDlg, 1 );
\r
8419 case IDC_NFG_Edit:
\r
8420 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8421 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8423 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8426 case IDC_NFG_Random:
\r
8427 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8428 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8441 int index = appData.defaultFrcPosition;
\r
8442 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8444 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8446 if( result == 0 ) {
\r
8447 appData.defaultFrcPosition = index;
\r
8453 /* [AS] Game list options. Refactored by HGM */
\r
8455 HWND gameListOptionsDialog;
\r
8457 // low-level front-end: clear text edit / list widget
\r
8461 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8464 // low-level front-end: clear text edit / list widget
\r
8466 GLT_DeSelectList()
\r
8468 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8471 // low-level front-end: append line to text edit / list widget
\r
8473 GLT_AddToList( char *name )
\r
8476 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8480 // low-level front-end: get line from text edit / list widget
\r
8482 GLT_GetFromList( int index, char *name )
\r
8485 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8491 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8493 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8494 int idx2 = idx1 + delta;
\r
8495 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8497 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8500 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8501 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8502 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8503 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8507 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8511 case WM_INITDIALOG:
\r
8512 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8514 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8515 Translate(hDlg, DLG_GameListOptions);
\r
8517 /* Initialize list */
\r
8518 GLT_TagsToList( lpUserGLT );
\r
8520 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8525 switch( LOWORD(wParam) ) {
\r
8528 EndDialog( hDlg, 0 );
\r
8531 EndDialog( hDlg, 1 );
\r
8534 case IDC_GLT_Default:
\r
8535 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8538 case IDC_GLT_Restore:
\r
8539 GLT_TagsToList( appData.gameListTags );
\r
8543 GLT_MoveSelection( hDlg, -1 );
\r
8546 case IDC_GLT_Down:
\r
8547 GLT_MoveSelection( hDlg, +1 );
\r
8557 int GameListOptions()
\r
8560 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8562 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8564 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8566 if( result == 0 ) {
\r
8567 /* [AS] Memory leak here! */
\r
8568 appData.gameListTags = strdup( lpUserGLT );
\r
8575 DisplayIcsInteractionTitle(char *str)
\r
8577 char consoleTitle[MSG_SIZ];
\r
8579 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8580 SetWindowText(hwndConsole, consoleTitle);
\r
8582 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8583 char buf[MSG_SIZ], *p = buf, *q;
\r
8584 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8586 q = strchr(p, ';');
\r
8588 if(*p) ChatPopUp(p);
\r
8592 SetActiveWindow(hwndMain);
\r
8596 DrawPosition(int fullRedraw, Board board)
\r
8598 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8601 void NotifyFrontendLogin()
\r
8604 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8610 fromX = fromY = -1;
\r
8611 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8612 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8613 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8614 dragInfo.lastpos = dragInfo.pos;
\r
8615 dragInfo.start.x = dragInfo.start.y = -1;
\r
8616 dragInfo.from = dragInfo.start;
\r
8618 DrawPosition(TRUE, NULL);
\r
8625 CommentPopUp(char *title, char *str)
\r
8627 HWND hwnd = GetActiveWindow();
\r
8628 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8630 SetActiveWindow(hwnd);
\r
8634 CommentPopDown(void)
\r
8636 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8637 if (commentDialog) {
\r
8638 ShowWindow(commentDialog, SW_HIDE);
\r
8640 commentUp = FALSE;
\r
8644 EditCommentPopUp(int index, char *title, char *str)
\r
8646 EitherCommentPopUp(index, title, str, TRUE);
\r
8653 MyPlaySound(&sounds[(int)SoundMove]);
\r
8656 VOID PlayIcsWinSound()
\r
8658 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8661 VOID PlayIcsLossSound()
\r
8663 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8666 VOID PlayIcsDrawSound()
\r
8668 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8671 VOID PlayIcsUnfinishedSound()
\r
8673 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8679 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8685 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8693 consoleEcho = TRUE;
\r
8694 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8695 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8696 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8705 consoleEcho = FALSE;
\r
8706 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8707 /* This works OK: set text and background both to the same color */
\r
8709 cf.crTextColor = COLOR_ECHOOFF;
\r
8710 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8711 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8714 /* No Raw()...? */
\r
8716 void Colorize(ColorClass cc, int continuation)
\r
8718 currentColorClass = cc;
\r
8719 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8720 consoleCF.crTextColor = textAttribs[cc].color;
\r
8721 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8722 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8728 static char buf[MSG_SIZ];
\r
8729 DWORD bufsiz = MSG_SIZ;
\r
8731 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8732 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8734 if (!GetUserName(buf, &bufsiz)) {
\r
8735 /*DisplayError("Error getting user name", GetLastError());*/
\r
8736 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8744 static char buf[MSG_SIZ];
\r
8745 DWORD bufsiz = MSG_SIZ;
\r
8747 if (!GetComputerName(buf, &bufsiz)) {
\r
8748 /*DisplayError("Error getting host name", GetLastError());*/
\r
8749 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8756 ClockTimerRunning()
\r
8758 return clockTimerEvent != 0;
\r
8764 if (clockTimerEvent == 0) return FALSE;
\r
8765 KillTimer(hwndMain, clockTimerEvent);
\r
8766 clockTimerEvent = 0;
\r
8771 StartClockTimer(long millisec)
\r
8773 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8774 (UINT) millisec, NULL);
\r
8778 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8781 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8783 if(appData.noGUI) return;
\r
8784 hdc = GetDC(hwndMain);
\r
8785 if (!IsIconic(hwndMain)) {
\r
8786 DisplayAClock(hdc, timeRemaining, highlight,
\r
8787 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8789 if (highlight && iconCurrent == iconBlack) {
\r
8790 iconCurrent = iconWhite;
\r
8791 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8792 if (IsIconic(hwndMain)) {
\r
8793 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8796 (void) ReleaseDC(hwndMain, hdc);
\r
8798 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8802 DisplayBlackClock(long timeRemaining, int highlight)
\r
8805 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8807 if(appData.noGUI) return;
\r
8808 hdc = GetDC(hwndMain);
\r
8809 if (!IsIconic(hwndMain)) {
\r
8810 DisplayAClock(hdc, timeRemaining, highlight,
\r
8811 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8813 if (highlight && iconCurrent == iconWhite) {
\r
8814 iconCurrent = iconBlack;
\r
8815 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8816 if (IsIconic(hwndMain)) {
\r
8817 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8820 (void) ReleaseDC(hwndMain, hdc);
\r
8822 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8827 LoadGameTimerRunning()
\r
8829 return loadGameTimerEvent != 0;
\r
8833 StopLoadGameTimer()
\r
8835 if (loadGameTimerEvent == 0) return FALSE;
\r
8836 KillTimer(hwndMain, loadGameTimerEvent);
\r
8837 loadGameTimerEvent = 0;
\r
8842 StartLoadGameTimer(long millisec)
\r
8844 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8845 (UINT) millisec, NULL);
\r
8853 char fileTitle[MSG_SIZ];
\r
8855 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8856 f = OpenFileDialog(hwndMain, "a", defName,
\r
8857 appData.oldSaveStyle ? "gam" : "pgn",
\r
8859 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8861 SaveGame(f, 0, "");
\r
8868 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8870 if (delayedTimerEvent != 0) {
\r
8871 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8872 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8874 KillTimer(hwndMain, delayedTimerEvent);
\r
8875 delayedTimerEvent = 0;
\r
8876 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8877 delayedTimerCallback();
\r
8879 delayedTimerCallback = cb;
\r
8880 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8881 (UINT) millisec, NULL);
\r
8884 DelayedEventCallback
\r
8887 if (delayedTimerEvent) {
\r
8888 return delayedTimerCallback;
\r
8895 CancelDelayedEvent()
\r
8897 if (delayedTimerEvent) {
\r
8898 KillTimer(hwndMain, delayedTimerEvent);
\r
8899 delayedTimerEvent = 0;
\r
8903 DWORD GetWin32Priority(int nice)
\r
8904 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8906 REALTIME_PRIORITY_CLASS 0x00000100
\r
8907 HIGH_PRIORITY_CLASS 0x00000080
\r
8908 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8909 NORMAL_PRIORITY_CLASS 0x00000020
\r
8910 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8911 IDLE_PRIORITY_CLASS 0x00000040
\r
8913 if (nice < -15) return 0x00000080;
\r
8914 if (nice < 0) return 0x00008000;
\r
8915 if (nice == 0) return 0x00000020;
\r
8916 if (nice < 15) return 0x00004000;
\r
8917 return 0x00000040;
\r
8920 void RunCommand(char *cmdLine)
\r
8922 /* Now create the child process. */
\r
8923 STARTUPINFO siStartInfo;
\r
8924 PROCESS_INFORMATION piProcInfo;
\r
8926 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8927 siStartInfo.lpReserved = NULL;
\r
8928 siStartInfo.lpDesktop = NULL;
\r
8929 siStartInfo.lpTitle = NULL;
\r
8930 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8931 siStartInfo.cbReserved2 = 0;
\r
8932 siStartInfo.lpReserved2 = NULL;
\r
8933 siStartInfo.hStdInput = NULL;
\r
8934 siStartInfo.hStdOutput = NULL;
\r
8935 siStartInfo.hStdError = NULL;
\r
8937 CreateProcess(NULL,
\r
8938 cmdLine, /* command line */
\r
8939 NULL, /* process security attributes */
\r
8940 NULL, /* primary thread security attrs */
\r
8941 TRUE, /* handles are inherited */
\r
8942 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8943 NULL, /* use parent's environment */
\r
8945 &siStartInfo, /* STARTUPINFO pointer */
\r
8946 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8948 CloseHandle(piProcInfo.hThread);
\r
8951 /* Start a child process running the given program.
\r
8952 The process's standard output can be read from "from", and its
\r
8953 standard input can be written to "to".
\r
8954 Exit with fatal error if anything goes wrong.
\r
8955 Returns an opaque pointer that can be used to destroy the process
\r
8959 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8961 #define BUFSIZE 4096
\r
8963 HANDLE hChildStdinRd, hChildStdinWr,
\r
8964 hChildStdoutRd, hChildStdoutWr;
\r
8965 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8966 SECURITY_ATTRIBUTES saAttr;
\r
8968 PROCESS_INFORMATION piProcInfo;
\r
8969 STARTUPINFO siStartInfo;
\r
8971 char buf[MSG_SIZ];
\r
8974 if (appData.debugMode) {
\r
8975 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8980 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8981 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8982 saAttr.bInheritHandle = TRUE;
\r
8983 saAttr.lpSecurityDescriptor = NULL;
\r
8986 * The steps for redirecting child's STDOUT:
\r
8987 * 1. Create anonymous pipe to be STDOUT for child.
\r
8988 * 2. Create a noninheritable duplicate of read handle,
\r
8989 * and close the inheritable read handle.
\r
8992 /* Create a pipe for the child's STDOUT. */
\r
8993 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8994 return GetLastError();
\r
8997 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8998 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8999 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9000 FALSE, /* not inherited */
\r
9001 DUPLICATE_SAME_ACCESS);
\r
9003 return GetLastError();
\r
9005 CloseHandle(hChildStdoutRd);
\r
9008 * The steps for redirecting child's STDIN:
\r
9009 * 1. Create anonymous pipe to be STDIN for child.
\r
9010 * 2. Create a noninheritable duplicate of write handle,
\r
9011 * and close the inheritable write handle.
\r
9014 /* Create a pipe for the child's STDIN. */
\r
9015 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9016 return GetLastError();
\r
9019 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9020 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9021 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9022 FALSE, /* not inherited */
\r
9023 DUPLICATE_SAME_ACCESS);
\r
9025 return GetLastError();
\r
9027 CloseHandle(hChildStdinWr);
\r
9029 /* Arrange to (1) look in dir for the child .exe file, and
\r
9030 * (2) have dir be the child's working directory. Interpret
\r
9031 * dir relative to the directory WinBoard loaded from. */
\r
9032 GetCurrentDirectory(MSG_SIZ, buf);
\r
9033 SetCurrentDirectory(installDir);
\r
9034 SetCurrentDirectory(dir);
\r
9036 /* Now create the child process. */
\r
9038 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9039 siStartInfo.lpReserved = NULL;
\r
9040 siStartInfo.lpDesktop = NULL;
\r
9041 siStartInfo.lpTitle = NULL;
\r
9042 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9043 siStartInfo.cbReserved2 = 0;
\r
9044 siStartInfo.lpReserved2 = NULL;
\r
9045 siStartInfo.hStdInput = hChildStdinRd;
\r
9046 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9047 siStartInfo.hStdError = hChildStdoutWr;
\r
9049 fSuccess = CreateProcess(NULL,
\r
9050 cmdLine, /* command line */
\r
9051 NULL, /* process security attributes */
\r
9052 NULL, /* primary thread security attrs */
\r
9053 TRUE, /* handles are inherited */
\r
9054 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9055 NULL, /* use parent's environment */
\r
9057 &siStartInfo, /* STARTUPINFO pointer */
\r
9058 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9060 err = GetLastError();
\r
9061 SetCurrentDirectory(buf); /* return to prev directory */
\r
9066 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9067 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9068 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9071 /* Close the handles we don't need in the parent */
\r
9072 CloseHandle(piProcInfo.hThread);
\r
9073 CloseHandle(hChildStdinRd);
\r
9074 CloseHandle(hChildStdoutWr);
\r
9076 /* Prepare return value */
\r
9077 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9078 cp->kind = CPReal;
\r
9079 cp->hProcess = piProcInfo.hProcess;
\r
9080 cp->pid = piProcInfo.dwProcessId;
\r
9081 cp->hFrom = hChildStdoutRdDup;
\r
9082 cp->hTo = hChildStdinWrDup;
\r
9084 *pr = (void *) cp;
\r
9086 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9087 2000 where engines sometimes don't see the initial command(s)
\r
9088 from WinBoard and hang. I don't understand how that can happen,
\r
9089 but the Sleep is harmless, so I've put it in. Others have also
\r
9090 reported what may be the same problem, so hopefully this will fix
\r
9091 it for them too. */
\r
9099 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9101 ChildProc *cp; int result;
\r
9103 cp = (ChildProc *) pr;
\r
9104 if (cp == NULL) return;
\r
9106 switch (cp->kind) {
\r
9108 /* TerminateProcess is considered harmful, so... */
\r
9109 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9110 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9111 /* The following doesn't work because the chess program
\r
9112 doesn't "have the same console" as WinBoard. Maybe
\r
9113 we could arrange for this even though neither WinBoard
\r
9114 nor the chess program uses a console for stdio? */
\r
9115 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9117 /* [AS] Special termination modes for misbehaving programs... */
\r
9118 if( signal == 9 ) {
\r
9119 result = TerminateProcess( cp->hProcess, 0 );
\r
9121 if ( appData.debugMode) {
\r
9122 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9125 else if( signal == 10 ) {
\r
9126 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9128 if( dw != WAIT_OBJECT_0 ) {
\r
9129 result = TerminateProcess( cp->hProcess, 0 );
\r
9131 if ( appData.debugMode) {
\r
9132 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9138 CloseHandle(cp->hProcess);
\r
9142 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9146 closesocket(cp->sock);
\r
9151 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9152 closesocket(cp->sock);
\r
9153 closesocket(cp->sock2);
\r
9161 InterruptChildProcess(ProcRef pr)
\r
9165 cp = (ChildProc *) pr;
\r
9166 if (cp == NULL) return;
\r
9167 switch (cp->kind) {
\r
9169 /* The following doesn't work because the chess program
\r
9170 doesn't "have the same console" as WinBoard. Maybe
\r
9171 we could arrange for this even though neither WinBoard
\r
9172 nor the chess program uses a console for stdio */
\r
9173 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9178 /* Can't interrupt */
\r
9182 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9189 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9191 char cmdLine[MSG_SIZ];
\r
9193 if (port[0] == NULLCHAR) {
\r
9194 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9196 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9198 return StartChildProcess(cmdLine, "", pr);
\r
9202 /* Code to open TCP sockets */
\r
9205 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9210 struct sockaddr_in sa, mysa;
\r
9211 struct hostent FAR *hp;
\r
9212 unsigned short uport;
\r
9213 WORD wVersionRequested;
\r
9216 /* Initialize socket DLL */
\r
9217 wVersionRequested = MAKEWORD(1, 1);
\r
9218 err = WSAStartup(wVersionRequested, &wsaData);
\r
9219 if (err != 0) return err;
\r
9222 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9223 err = WSAGetLastError();
\r
9228 /* Bind local address using (mostly) don't-care values.
\r
9230 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9231 mysa.sin_family = AF_INET;
\r
9232 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9233 uport = (unsigned short) 0;
\r
9234 mysa.sin_port = htons(uport);
\r
9235 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9236 == SOCKET_ERROR) {
\r
9237 err = WSAGetLastError();
\r
9242 /* Resolve remote host name */
\r
9243 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9244 if (!(hp = gethostbyname(host))) {
\r
9245 unsigned int b0, b1, b2, b3;
\r
9247 err = WSAGetLastError();
\r
9249 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9250 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9251 hp->h_addrtype = AF_INET;
\r
9253 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9254 hp->h_addr_list[0] = (char *) malloc(4);
\r
9255 hp->h_addr_list[0][0] = (char) b0;
\r
9256 hp->h_addr_list[0][1] = (char) b1;
\r
9257 hp->h_addr_list[0][2] = (char) b2;
\r
9258 hp->h_addr_list[0][3] = (char) b3;
\r
9264 sa.sin_family = hp->h_addrtype;
\r
9265 uport = (unsigned short) atoi(port);
\r
9266 sa.sin_port = htons(uport);
\r
9267 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9269 /* Make connection */
\r
9270 if (connect(s, (struct sockaddr *) &sa,
\r
9271 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9272 err = WSAGetLastError();
\r
9277 /* Prepare return value */
\r
9278 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9279 cp->kind = CPSock;
\r
9281 *pr = (ProcRef *) cp;
\r
9287 OpenCommPort(char *name, ProcRef *pr)
\r
9292 char fullname[MSG_SIZ];
\r
9294 if (*name != '\\')
\r
9295 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9297 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9299 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9300 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9301 if (h == (HANDLE) -1) {
\r
9302 return GetLastError();
\r
9306 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9308 /* Accumulate characters until a 100ms pause, then parse */
\r
9309 ct.ReadIntervalTimeout = 100;
\r
9310 ct.ReadTotalTimeoutMultiplier = 0;
\r
9311 ct.ReadTotalTimeoutConstant = 0;
\r
9312 ct.WriteTotalTimeoutMultiplier = 0;
\r
9313 ct.WriteTotalTimeoutConstant = 0;
\r
9314 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9316 /* Prepare return value */
\r
9317 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9318 cp->kind = CPComm;
\r
9321 *pr = (ProcRef *) cp;
\r
9327 OpenLoopback(ProcRef *pr)
\r
9329 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9335 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9340 struct sockaddr_in sa, mysa;
\r
9341 struct hostent FAR *hp;
\r
9342 unsigned short uport;
\r
9343 WORD wVersionRequested;
\r
9346 char stderrPortStr[MSG_SIZ];
\r
9348 /* Initialize socket DLL */
\r
9349 wVersionRequested = MAKEWORD(1, 1);
\r
9350 err = WSAStartup(wVersionRequested, &wsaData);
\r
9351 if (err != 0) return err;
\r
9353 /* Resolve remote host name */
\r
9354 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9355 if (!(hp = gethostbyname(host))) {
\r
9356 unsigned int b0, b1, b2, b3;
\r
9358 err = WSAGetLastError();
\r
9360 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9361 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9362 hp->h_addrtype = AF_INET;
\r
9364 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9365 hp->h_addr_list[0] = (char *) malloc(4);
\r
9366 hp->h_addr_list[0][0] = (char) b0;
\r
9367 hp->h_addr_list[0][1] = (char) b1;
\r
9368 hp->h_addr_list[0][2] = (char) b2;
\r
9369 hp->h_addr_list[0][3] = (char) b3;
\r
9375 sa.sin_family = hp->h_addrtype;
\r
9376 uport = (unsigned short) 514;
\r
9377 sa.sin_port = htons(uport);
\r
9378 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9380 /* Bind local socket to unused "privileged" port address
\r
9382 s = INVALID_SOCKET;
\r
9383 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9384 mysa.sin_family = AF_INET;
\r
9385 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9386 for (fromPort = 1023;; fromPort--) {
\r
9387 if (fromPort < 0) {
\r
9389 return WSAEADDRINUSE;
\r
9391 if (s == INVALID_SOCKET) {
\r
9392 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9393 err = WSAGetLastError();
\r
9398 uport = (unsigned short) fromPort;
\r
9399 mysa.sin_port = htons(uport);
\r
9400 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9401 == SOCKET_ERROR) {
\r
9402 err = WSAGetLastError();
\r
9403 if (err == WSAEADDRINUSE) continue;
\r
9407 if (connect(s, (struct sockaddr *) &sa,
\r
9408 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9409 err = WSAGetLastError();
\r
9410 if (err == WSAEADDRINUSE) {
\r
9421 /* Bind stderr local socket to unused "privileged" port address
\r
9423 s2 = INVALID_SOCKET;
\r
9424 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9425 mysa.sin_family = AF_INET;
\r
9426 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9427 for (fromPort = 1023;; fromPort--) {
\r
9428 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9429 if (fromPort < 0) {
\r
9430 (void) closesocket(s);
\r
9432 return WSAEADDRINUSE;
\r
9434 if (s2 == INVALID_SOCKET) {
\r
9435 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9436 err = WSAGetLastError();
\r
9442 uport = (unsigned short) fromPort;
\r
9443 mysa.sin_port = htons(uport);
\r
9444 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9445 == SOCKET_ERROR) {
\r
9446 err = WSAGetLastError();
\r
9447 if (err == WSAEADDRINUSE) continue;
\r
9448 (void) closesocket(s);
\r
9452 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9453 err = WSAGetLastError();
\r
9454 if (err == WSAEADDRINUSE) {
\r
9456 s2 = INVALID_SOCKET;
\r
9459 (void) closesocket(s);
\r
9460 (void) closesocket(s2);
\r
9466 prevStderrPort = fromPort; // remember port used
\r
9467 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9469 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9470 err = WSAGetLastError();
\r
9471 (void) closesocket(s);
\r
9472 (void) closesocket(s2);
\r
9477 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9478 err = WSAGetLastError();
\r
9479 (void) closesocket(s);
\r
9480 (void) closesocket(s2);
\r
9484 if (*user == NULLCHAR) user = UserName();
\r
9485 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9486 err = WSAGetLastError();
\r
9487 (void) closesocket(s);
\r
9488 (void) closesocket(s2);
\r
9492 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9493 err = WSAGetLastError();
\r
9494 (void) closesocket(s);
\r
9495 (void) closesocket(s2);
\r
9500 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9501 err = WSAGetLastError();
\r
9502 (void) closesocket(s);
\r
9503 (void) closesocket(s2);
\r
9507 (void) closesocket(s2); /* Stop listening */
\r
9509 /* Prepare return value */
\r
9510 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9511 cp->kind = CPRcmd;
\r
9514 *pr = (ProcRef *) cp;
\r
9521 AddInputSource(ProcRef pr, int lineByLine,
\r
9522 InputCallback func, VOIDSTAR closure)
\r
9524 InputSource *is, *is2 = NULL;
\r
9525 ChildProc *cp = (ChildProc *) pr;
\r
9527 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9528 is->lineByLine = lineByLine;
\r
9530 is->closure = closure;
\r
9531 is->second = NULL;
\r
9532 is->next = is->buf;
\r
9533 if (pr == NoProc) {
\r
9534 is->kind = CPReal;
\r
9535 consoleInputSource = is;
\r
9537 is->kind = cp->kind;
\r
9539 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9540 we create all threads suspended so that the is->hThread variable can be
\r
9541 safely assigned, then let the threads start with ResumeThread.
\r
9543 switch (cp->kind) {
\r
9545 is->hFile = cp->hFrom;
\r
9546 cp->hFrom = NULL; /* now owned by InputThread */
\r
9548 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9549 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9553 is->hFile = cp->hFrom;
\r
9554 cp->hFrom = NULL; /* now owned by InputThread */
\r
9556 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9557 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9561 is->sock = cp->sock;
\r
9563 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9564 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9568 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9570 is->sock = cp->sock;
\r
9572 is2->sock = cp->sock2;
\r
9573 is2->second = is2;
\r
9575 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9576 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9578 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9579 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9583 if( is->hThread != NULL ) {
\r
9584 ResumeThread( is->hThread );
\r
9587 if( is2 != NULL && is2->hThread != NULL ) {
\r
9588 ResumeThread( is2->hThread );
\r
9592 return (InputSourceRef) is;
\r
9596 RemoveInputSource(InputSourceRef isr)
\r
9600 is = (InputSource *) isr;
\r
9601 is->hThread = NULL; /* tell thread to stop */
\r
9602 CloseHandle(is->hThread);
\r
9603 if (is->second != NULL) {
\r
9604 is->second->hThread = NULL;
\r
9605 CloseHandle(is->second->hThread);
\r
9609 int no_wrap(char *message, int count)
\r
9611 ConsoleOutput(message, count, FALSE);
\r
9616 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9619 int outCount = SOCKET_ERROR;
\r
9620 ChildProc *cp = (ChildProc *) pr;
\r
9621 static OVERLAPPED ovl;
\r
9622 static int line = 0;
\r
9626 if (appData.noJoin || !appData.useInternalWrap)
\r
9627 return no_wrap(message, count);
\r
9630 int width = get_term_width();
\r
9631 int len = wrap(NULL, message, count, width, &line);
\r
9632 char *msg = malloc(len);
\r
9636 return no_wrap(message, count);
\r
9639 dbgchk = wrap(msg, message, count, width, &line);
\r
9640 if (dbgchk != len && appData.debugMode)
\r
9641 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9642 ConsoleOutput(msg, len, FALSE);
\r
9649 if (ovl.hEvent == NULL) {
\r
9650 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9652 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9654 switch (cp->kind) {
\r
9657 outCount = send(cp->sock, message, count, 0);
\r
9658 if (outCount == SOCKET_ERROR) {
\r
9659 *outError = WSAGetLastError();
\r
9661 *outError = NO_ERROR;
\r
9666 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9667 &dOutCount, NULL)) {
\r
9668 *outError = NO_ERROR;
\r
9669 outCount = (int) dOutCount;
\r
9671 *outError = GetLastError();
\r
9676 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9677 &dOutCount, &ovl);
\r
9678 if (*outError == NO_ERROR) {
\r
9679 outCount = (int) dOutCount;
\r
9689 if(n != 0) Sleep(n);
\r
9693 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9696 /* Ignore delay, not implemented for WinBoard */
\r
9697 return OutputToProcess(pr, message, count, outError);
\r
9702 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9703 char *buf, int count, int error)
\r
9705 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9708 /* see wgamelist.c for Game List functions */
\r
9709 /* see wedittags.c for Edit Tags functions */
\r
9716 char buf[MSG_SIZ];
\r
9719 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9720 f = fopen(buf, "r");
\r
9722 ProcessICSInitScript(f);
\r
9730 StartAnalysisClock()
\r
9732 if (analysisTimerEvent) return;
\r
9733 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9734 (UINT) 2000, NULL);
\r
9738 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9740 highlightInfo.sq[0].x = fromX;
\r
9741 highlightInfo.sq[0].y = fromY;
\r
9742 highlightInfo.sq[1].x = toX;
\r
9743 highlightInfo.sq[1].y = toY;
\r
9749 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9750 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9754 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9756 premoveHighlightInfo.sq[0].x = fromX;
\r
9757 premoveHighlightInfo.sq[0].y = fromY;
\r
9758 premoveHighlightInfo.sq[1].x = toX;
\r
9759 premoveHighlightInfo.sq[1].y = toY;
\r
9763 ClearPremoveHighlights()
\r
9765 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9766 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9770 ShutDownFrontEnd()
\r
9772 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9773 DeleteClipboardTempFiles();
\r
9779 if (IsIconic(hwndMain))
\r
9780 ShowWindow(hwndMain, SW_RESTORE);
\r
9782 SetActiveWindow(hwndMain);
\r
9786 * Prototypes for animation support routines
\r
9788 static void ScreenSquare(int column, int row, POINT * pt);
\r
9789 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9790 POINT frames[], int * nFrames);
\r
9796 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9797 { // [HGM] atomic: animate blast wave
\r
9800 explodeInfo.fromX = fromX;
\r
9801 explodeInfo.fromY = fromY;
\r
9802 explodeInfo.toX = toX;
\r
9803 explodeInfo.toY = toY;
\r
9804 for(i=1; i<4*kFactor; i++) {
\r
9805 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9806 DrawPosition(FALSE, board);
\r
9807 Sleep(appData.animSpeed);
\r
9809 explodeInfo.radius = 0;
\r
9810 DrawPosition(TRUE, board);
\r
9814 AnimateMove(board, fromX, fromY, toX, toY)
\r
9821 ChessSquare piece;
\r
9822 POINT start, finish, mid;
\r
9823 POINT frames[kFactor * 2 + 1];
\r
9826 if (!appData.animate) return;
\r
9827 if (doingSizing) return;
\r
9828 if (fromY < 0 || fromX < 0) return;
\r
9829 piece = board[fromY][fromX];
\r
9830 if (piece >= EmptySquare) return;
\r
9832 ScreenSquare(fromX, fromY, &start);
\r
9833 ScreenSquare(toX, toY, &finish);
\r
9835 /* All moves except knight jumps move in straight line */
\r
9836 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9837 mid.x = start.x + (finish.x - start.x) / 2;
\r
9838 mid.y = start.y + (finish.y - start.y) / 2;
\r
9840 /* Knight: make straight movement then diagonal */
\r
9841 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9842 mid.x = start.x + (finish.x - start.x) / 2;
\r
9846 mid.y = start.y + (finish.y - start.y) / 2;
\r
9850 /* Don't use as many frames for very short moves */
\r
9851 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9852 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9854 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9856 animInfo.from.x = fromX;
\r
9857 animInfo.from.y = fromY;
\r
9858 animInfo.to.x = toX;
\r
9859 animInfo.to.y = toY;
\r
9860 animInfo.lastpos = start;
\r
9861 animInfo.piece = piece;
\r
9862 for (n = 0; n < nFrames; n++) {
\r
9863 animInfo.pos = frames[n];
\r
9864 DrawPosition(FALSE, NULL);
\r
9865 animInfo.lastpos = animInfo.pos;
\r
9866 Sleep(appData.animSpeed);
\r
9868 animInfo.pos = finish;
\r
9869 DrawPosition(FALSE, NULL);
\r
9870 animInfo.piece = EmptySquare;
\r
9871 Explode(board, fromX, fromY, toX, toY);
\r
9874 /* Convert board position to corner of screen rect and color */
\r
9877 ScreenSquare(column, row, pt)
\r
9878 int column; int row; POINT * pt;
\r
9881 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9882 pt->y = lineGap + row * (squareSize + lineGap);
\r
9884 pt->x = lineGap + column * (squareSize + lineGap);
\r
9885 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9889 /* Generate a series of frame coords from start->mid->finish.
\r
9890 The movement rate doubles until the half way point is
\r
9891 reached, then halves back down to the final destination,
\r
9892 which gives a nice slow in/out effect. The algorithmn
\r
9893 may seem to generate too many intermediates for short
\r
9894 moves, but remember that the purpose is to attract the
\r
9895 viewers attention to the piece about to be moved and
\r
9896 then to where it ends up. Too few frames would be less
\r
9900 Tween(start, mid, finish, factor, frames, nFrames)
\r
9901 POINT * start; POINT * mid;
\r
9902 POINT * finish; int factor;
\r
9903 POINT frames[]; int * nFrames;
\r
9905 int n, fraction = 1, count = 0;
\r
9907 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9908 for (n = 0; n < factor; n++)
\r
9910 for (n = 0; n < factor; n++) {
\r
9911 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9912 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9914 fraction = fraction / 2;
\r
9918 frames[count] = *mid;
\r
9921 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9923 for (n = 0; n < factor; n++) {
\r
9924 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9925 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9927 fraction = fraction * 2;
\r
9933 SettingsPopUp(ChessProgramState *cps)
\r
9934 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9935 EngineOptionsPopup(savedHwnd, cps);
\r
9938 int flock(int fid, int code)
\r
9940 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9944 ov.OffsetHigh = 0;
\r
9946 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9947 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9948 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9949 default: return -1;
\r