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
1197 #define TOPLEVEL 0
\r
1201 // front-end part of option handling
\r
1204 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1206 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1207 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1210 lf->lfEscapement = 0;
\r
1211 lf->lfOrientation = 0;
\r
1212 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1213 lf->lfItalic = mfp->italic;
\r
1214 lf->lfUnderline = mfp->underline;
\r
1215 lf->lfStrikeOut = mfp->strikeout;
\r
1216 lf->lfCharSet = mfp->charset;
\r
1217 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1218 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1219 lf->lfQuality = DEFAULT_QUALITY;
\r
1220 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1221 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1225 CreateFontInMF(MyFont *mf)
\r
1227 LFfromMFP(&mf->lf, &mf->mfp);
\r
1228 if (mf->hf) DeleteObject(mf->hf);
\r
1229 mf->hf = CreateFontIndirect(&mf->lf);
\r
1232 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1234 colorVariable[] = {
\r
1235 &whitePieceColor,
\r
1236 &blackPieceColor,
\r
1237 &lightSquareColor,
\r
1238 &darkSquareColor,
\r
1239 &highlightSquareColor,
\r
1240 &premoveHighlightColor,
\r
1242 &consoleBackgroundColor,
\r
1243 &appData.fontForeColorWhite,
\r
1244 &appData.fontBackColorWhite,
\r
1245 &appData.fontForeColorBlack,
\r
1246 &appData.fontBackColorBlack,
\r
1247 &appData.evalHistColorWhite,
\r
1248 &appData.evalHistColorBlack,
\r
1249 &appData.highlightArrowColor,
\r
1252 /* Command line font name parser. NULL name means do nothing.
\r
1253 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1254 For backward compatibility, syntax without the colon is also
\r
1255 accepted, but font names with digits in them won't work in that case.
\r
1258 ParseFontName(char *name, MyFontParams *mfp)
\r
1261 if (name == NULL) return;
\r
1263 q = strchr(p, ':');
\r
1265 if (q - p >= sizeof(mfp->faceName))
\r
1266 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1267 memcpy(mfp->faceName, p, q - p);
\r
1268 mfp->faceName[q - p] = NULLCHAR;
\r
1271 q = mfp->faceName;
\r
1272 while (*p && !isdigit(*p)) {
\r
1274 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1275 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1277 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1280 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1281 mfp->pointSize = (float) atof(p);
\r
1282 mfp->bold = (strchr(p, 'b') != NULL);
\r
1283 mfp->italic = (strchr(p, 'i') != NULL);
\r
1284 mfp->underline = (strchr(p, 'u') != NULL);
\r
1285 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1286 mfp->charset = DEFAULT_CHARSET;
\r
1287 q = strchr(p, 'c');
\r
1289 mfp->charset = (BYTE) atoi(q+1);
\r
1293 ParseFont(char *name, int number)
\r
1294 { // wrapper to shield back-end from 'font'
\r
1295 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1300 { // in WB we have a 2D array of fonts; this initializes their description
\r
1302 /* Point font array elements to structures and
\r
1303 parse default font names */
\r
1304 for (i=0; i<NUM_FONTS; i++) {
\r
1305 for (j=0; j<NUM_SIZES; j++) {
\r
1306 font[j][i] = &fontRec[j][i];
\r
1307 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1314 { // here we create the actual fonts from the selected descriptions
\r
1316 for (i=0; i<NUM_FONTS; i++) {
\r
1317 for (j=0; j<NUM_SIZES; j++) {
\r
1318 CreateFontInMF(font[j][i]);
\r
1322 /* Color name parser.
\r
1323 X version accepts X color names, but this one
\r
1324 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1326 ParseColorName(char *name)
\r
1328 int red, green, blue, count;
\r
1329 char buf[MSG_SIZ];
\r
1331 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1333 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1334 &red, &green, &blue);
\r
1337 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1338 DisplayError(buf, 0);
\r
1339 return RGB(0, 0, 0);
\r
1341 return PALETTERGB(red, green, blue);
\r
1345 ParseColor(int n, char *name)
\r
1346 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1347 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1351 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1353 char *e = argValue;
\r
1357 if (*e == 'b') eff |= CFE_BOLD;
\r
1358 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1359 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1360 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1361 else if (*e == '#' || isdigit(*e)) break;
\r
1365 *color = ParseColorName(e);
\r
1369 ParseTextAttribs(ColorClass cc, char *s)
\r
1370 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1371 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1372 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1376 ParseBoardSize(void *addr, char *name)
\r
1377 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1378 BoardSize bs = SizeTiny;
\r
1379 while (sizeInfo[bs].name != NULL) {
\r
1380 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1381 *(BoardSize *)addr = bs;
\r
1386 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1391 { // [HGM] import name from appData first
\r
1394 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1395 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1396 textAttribs[cc].sound.data = NULL;
\r
1397 MyLoadSound(&textAttribs[cc].sound);
\r
1399 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1400 textAttribs[cc].sound.name = strdup("");
\r
1401 textAttribs[cc].sound.data = NULL;
\r
1403 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1404 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1405 sounds[sc].data = NULL;
\r
1406 MyLoadSound(&sounds[sc]);
\r
1411 SetCommPortDefaults()
\r
1413 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1414 dcb.DCBlength = sizeof(DCB);
\r
1415 dcb.BaudRate = 9600;
\r
1416 dcb.fBinary = TRUE;
\r
1417 dcb.fParity = FALSE;
\r
1418 dcb.fOutxCtsFlow = FALSE;
\r
1419 dcb.fOutxDsrFlow = FALSE;
\r
1420 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1421 dcb.fDsrSensitivity = FALSE;
\r
1422 dcb.fTXContinueOnXoff = TRUE;
\r
1423 dcb.fOutX = FALSE;
\r
1425 dcb.fNull = FALSE;
\r
1426 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1427 dcb.fAbortOnError = FALSE;
\r
1429 dcb.Parity = SPACEPARITY;
\r
1430 dcb.StopBits = ONESTOPBIT;
\r
1433 // [HGM] args: these three cases taken out to stay in front-end
\r
1435 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1436 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1437 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1438 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1440 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1441 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1442 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1443 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1444 ad->argName, mfp->faceName, mfp->pointSize,
\r
1445 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1446 mfp->bold ? "b" : "",
\r
1447 mfp->italic ? "i" : "",
\r
1448 mfp->underline ? "u" : "",
\r
1449 mfp->strikeout ? "s" : "",
\r
1450 (int)mfp->charset);
\r
1456 { // [HGM] copy the names from the internal WB variables to appData
\r
1459 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1460 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1461 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1462 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1466 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1467 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1468 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1469 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1470 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1471 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1472 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1473 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1474 (ta->effects) ? " " : "",
\r
1475 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1479 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1480 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1481 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1482 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1483 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1487 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1488 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1489 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1493 ParseCommPortSettings(char *s)
\r
1494 { // wrapper to keep dcb from back-end
\r
1495 ParseCommSettings(s, &dcb);
\r
1500 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1501 GetActualPlacement(hwndMain, &wpMain);
\r
1502 GetActualPlacement(hwndConsole, &wpConsole);
\r
1503 GetActualPlacement(commentDialog, &wpComment);
\r
1504 GetActualPlacement(editTagsDialog, &wpTags);
\r
1505 GetActualPlacement(gameListDialog, &wpGameList);
\r
1506 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1507 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1508 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1512 PrintCommPortSettings(FILE *f, char *name)
\r
1513 { // wrapper to shield back-end from DCB
\r
1514 PrintCommSettings(f, name, &dcb);
\r
1518 MySearchPath(char *installDir, char *name, char *fullname)
\r
1520 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1521 if(name[0]== '%') {
\r
1522 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1523 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1524 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1525 *strchr(buf, '%') = 0;
\r
1526 strcat(fullname, getenv(buf));
\r
1527 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1529 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1530 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1531 return (int) strlen(fullname);
\r
1533 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1537 MyGetFullPathName(char *name, char *fullname)
\r
1540 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1545 { // [HGM] args: allows testing if main window is realized from back-end
\r
1546 return hwndMain != NULL;
\r
1550 PopUpStartupDialog()
\r
1554 LoadLanguageFile(appData.language);
\r
1555 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1556 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1557 FreeProcInstance(lpProc);
\r
1560 /*---------------------------------------------------------------------------*\
\r
1562 * GDI board drawing routines
\r
1564 \*---------------------------------------------------------------------------*/
\r
1566 /* [AS] Draw square using background texture */
\r
1567 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1572 return; /* Should never happen! */
\r
1575 SetGraphicsMode( dst, GM_ADVANCED );
\r
1582 /* X reflection */
\r
1587 x.eDx = (FLOAT) dw + dx - 1;
\r
1590 SetWorldTransform( dst, &x );
\r
1593 /* Y reflection */
\r
1599 x.eDy = (FLOAT) dh + dy - 1;
\r
1601 SetWorldTransform( dst, &x );
\r
1609 x.eDx = (FLOAT) dx;
\r
1610 x.eDy = (FLOAT) dy;
\r
1613 SetWorldTransform( dst, &x );
\r
1617 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1625 SetWorldTransform( dst, &x );
\r
1627 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1630 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1632 PM_WP = (int) WhitePawn,
\r
1633 PM_WN = (int) WhiteKnight,
\r
1634 PM_WB = (int) WhiteBishop,
\r
1635 PM_WR = (int) WhiteRook,
\r
1636 PM_WQ = (int) WhiteQueen,
\r
1637 PM_WF = (int) WhiteFerz,
\r
1638 PM_WW = (int) WhiteWazir,
\r
1639 PM_WE = (int) WhiteAlfil,
\r
1640 PM_WM = (int) WhiteMan,
\r
1641 PM_WO = (int) WhiteCannon,
\r
1642 PM_WU = (int) WhiteUnicorn,
\r
1643 PM_WH = (int) WhiteNightrider,
\r
1644 PM_WA = (int) WhiteAngel,
\r
1645 PM_WC = (int) WhiteMarshall,
\r
1646 PM_WAB = (int) WhiteCardinal,
\r
1647 PM_WD = (int) WhiteDragon,
\r
1648 PM_WL = (int) WhiteLance,
\r
1649 PM_WS = (int) WhiteCobra,
\r
1650 PM_WV = (int) WhiteFalcon,
\r
1651 PM_WSG = (int) WhiteSilver,
\r
1652 PM_WG = (int) WhiteGrasshopper,
\r
1653 PM_WK = (int) WhiteKing,
\r
1654 PM_BP = (int) BlackPawn,
\r
1655 PM_BN = (int) BlackKnight,
\r
1656 PM_BB = (int) BlackBishop,
\r
1657 PM_BR = (int) BlackRook,
\r
1658 PM_BQ = (int) BlackQueen,
\r
1659 PM_BF = (int) BlackFerz,
\r
1660 PM_BW = (int) BlackWazir,
\r
1661 PM_BE = (int) BlackAlfil,
\r
1662 PM_BM = (int) BlackMan,
\r
1663 PM_BO = (int) BlackCannon,
\r
1664 PM_BU = (int) BlackUnicorn,
\r
1665 PM_BH = (int) BlackNightrider,
\r
1666 PM_BA = (int) BlackAngel,
\r
1667 PM_BC = (int) BlackMarshall,
\r
1668 PM_BG = (int) BlackGrasshopper,
\r
1669 PM_BAB = (int) BlackCardinal,
\r
1670 PM_BD = (int) BlackDragon,
\r
1671 PM_BL = (int) BlackLance,
\r
1672 PM_BS = (int) BlackCobra,
\r
1673 PM_BV = (int) BlackFalcon,
\r
1674 PM_BSG = (int) BlackSilver,
\r
1675 PM_BK = (int) BlackKing
\r
1678 static HFONT hPieceFont = NULL;
\r
1679 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1680 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1681 static int fontBitmapSquareSize = 0;
\r
1682 static char pieceToFontChar[(int) EmptySquare] =
\r
1683 { 'p', 'n', 'b', 'r', 'q',
\r
1684 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1685 'k', 'o', 'm', 'v', 't', 'w',
\r
1686 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1689 extern BOOL SetCharTable( char *table, const char * map );
\r
1690 /* [HGM] moved to backend.c */
\r
1692 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1695 BYTE r1 = GetRValue( color );
\r
1696 BYTE g1 = GetGValue( color );
\r
1697 BYTE b1 = GetBValue( color );
\r
1703 /* Create a uniform background first */
\r
1704 hbrush = CreateSolidBrush( color );
\r
1705 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1706 FillRect( hdc, &rc, hbrush );
\r
1707 DeleteObject( hbrush );
\r
1710 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1711 int steps = squareSize / 2;
\r
1714 for( i=0; i<steps; i++ ) {
\r
1715 BYTE r = r1 - (r1-r2) * i / steps;
\r
1716 BYTE g = g1 - (g1-g2) * i / steps;
\r
1717 BYTE b = b1 - (b1-b2) * i / steps;
\r
1719 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1720 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1721 FillRect( hdc, &rc, hbrush );
\r
1722 DeleteObject(hbrush);
\r
1725 else if( mode == 2 ) {
\r
1726 /* Diagonal gradient, good more or less for every piece */
\r
1727 POINT triangle[3];
\r
1728 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1729 HBRUSH hbrush_old;
\r
1730 int steps = squareSize;
\r
1733 triangle[0].x = squareSize - steps;
\r
1734 triangle[0].y = squareSize;
\r
1735 triangle[1].x = squareSize;
\r
1736 triangle[1].y = squareSize;
\r
1737 triangle[2].x = squareSize;
\r
1738 triangle[2].y = squareSize - steps;
\r
1740 for( i=0; i<steps; i++ ) {
\r
1741 BYTE r = r1 - (r1-r2) * i / steps;
\r
1742 BYTE g = g1 - (g1-g2) * i / steps;
\r
1743 BYTE b = b1 - (b1-b2) * i / steps;
\r
1745 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1746 hbrush_old = SelectObject( hdc, hbrush );
\r
1747 Polygon( hdc, triangle, 3 );
\r
1748 SelectObject( hdc, hbrush_old );
\r
1749 DeleteObject(hbrush);
\r
1754 SelectObject( hdc, hpen );
\r
1759 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1760 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1761 piece: follow the steps as explained below.
\r
1763 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1767 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1771 int backColor = whitePieceColor;
\r
1772 int foreColor = blackPieceColor;
\r
1774 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1775 backColor = appData.fontBackColorWhite;
\r
1776 foreColor = appData.fontForeColorWhite;
\r
1778 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1779 backColor = appData.fontBackColorBlack;
\r
1780 foreColor = appData.fontForeColorBlack;
\r
1784 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1786 hbm_old = SelectObject( hdc, hbm );
\r
1790 rc.right = squareSize;
\r
1791 rc.bottom = squareSize;
\r
1793 /* Step 1: background is now black */
\r
1794 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1796 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1798 pt.x = (squareSize - sz.cx) / 2;
\r
1799 pt.y = (squareSize - sz.cy) / 2;
\r
1801 SetBkMode( hdc, TRANSPARENT );
\r
1802 SetTextColor( hdc, chroma );
\r
1803 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1804 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1806 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1807 /* Step 3: the area outside the piece is filled with white */
\r
1808 // FloodFill( hdc, 0, 0, chroma );
\r
1809 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1810 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1811 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1812 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1813 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1815 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1816 but if the start point is not inside the piece we're lost!
\r
1817 There should be a better way to do this... if we could create a region or path
\r
1818 from the fill operation we would be fine for example.
\r
1820 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1821 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1823 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1824 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1825 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1827 SelectObject( dc2, bm2 );
\r
1828 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1829 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1830 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1831 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1832 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1835 DeleteObject( bm2 );
\r
1838 SetTextColor( hdc, 0 );
\r
1840 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1841 draw the piece again in black for safety.
\r
1843 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1845 SelectObject( hdc, hbm_old );
\r
1847 if( hPieceMask[index] != NULL ) {
\r
1848 DeleteObject( hPieceMask[index] );
\r
1851 hPieceMask[index] = hbm;
\r
1854 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1856 SelectObject( hdc, hbm );
\r
1859 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1860 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1861 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1863 SelectObject( dc1, hPieceMask[index] );
\r
1864 SelectObject( dc2, bm2 );
\r
1865 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1866 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1869 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1870 the piece background and deletes (makes transparent) the rest.
\r
1871 Thanks to that mask, we are free to paint the background with the greates
\r
1872 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1873 We use this, to make gradients and give the pieces a "roundish" look.
\r
1875 SetPieceBackground( hdc, backColor, 2 );
\r
1876 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1880 DeleteObject( bm2 );
\r
1883 SetTextColor( hdc, foreColor );
\r
1884 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1886 SelectObject( hdc, hbm_old );
\r
1888 if( hPieceFace[index] != NULL ) {
\r
1889 DeleteObject( hPieceFace[index] );
\r
1892 hPieceFace[index] = hbm;
\r
1895 static int TranslatePieceToFontPiece( int piece )
\r
1925 case BlackMarshall:
\r
1929 case BlackNightrider:
\r
1935 case BlackUnicorn:
\r
1939 case BlackGrasshopper:
\r
1951 case BlackCardinal:
\r
1958 case WhiteMarshall:
\r
1962 case WhiteNightrider:
\r
1968 case WhiteUnicorn:
\r
1972 case WhiteGrasshopper:
\r
1984 case WhiteCardinal:
\r
1993 void CreatePiecesFromFont()
\r
1996 HDC hdc_window = NULL;
\r
2002 if( fontBitmapSquareSize < 0 ) {
\r
2003 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2007 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2008 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2009 fontBitmapSquareSize = -1;
\r
2013 if( fontBitmapSquareSize != squareSize ) {
\r
2014 hdc_window = GetDC( hwndMain );
\r
2015 hdc = CreateCompatibleDC( hdc_window );
\r
2017 if( hPieceFont != NULL ) {
\r
2018 DeleteObject( hPieceFont );
\r
2021 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2022 hPieceMask[i] = NULL;
\r
2023 hPieceFace[i] = NULL;
\r
2029 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2030 fontHeight = appData.fontPieceSize;
\r
2033 fontHeight = (fontHeight * squareSize) / 100;
\r
2035 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2037 lf.lfEscapement = 0;
\r
2038 lf.lfOrientation = 0;
\r
2039 lf.lfWeight = FW_NORMAL;
\r
2041 lf.lfUnderline = 0;
\r
2042 lf.lfStrikeOut = 0;
\r
2043 lf.lfCharSet = DEFAULT_CHARSET;
\r
2044 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2045 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2046 lf.lfQuality = PROOF_QUALITY;
\r
2047 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2048 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2049 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2051 hPieceFont = CreateFontIndirect( &lf );
\r
2053 if( hPieceFont == NULL ) {
\r
2054 fontBitmapSquareSize = -2;
\r
2057 /* Setup font-to-piece character table */
\r
2058 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2059 /* No (or wrong) global settings, try to detect the font */
\r
2060 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2062 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2064 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2065 /* DiagramTT* family */
\r
2066 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2068 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2069 /* Fairy symbols */
\r
2070 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2072 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2073 /* Good Companion (Some characters get warped as literal :-( */
\r
2074 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2075 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2076 SetCharTable(pieceToFontChar, s);
\r
2079 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2080 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2084 /* Create bitmaps */
\r
2085 hfont_old = SelectObject( hdc, hPieceFont );
\r
2086 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2087 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2088 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2090 SelectObject( hdc, hfont_old );
\r
2092 fontBitmapSquareSize = squareSize;
\r
2096 if( hdc != NULL ) {
\r
2100 if( hdc_window != NULL ) {
\r
2101 ReleaseDC( hwndMain, hdc_window );
\r
2106 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2110 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2111 if (gameInfo.event &&
\r
2112 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2113 strcmp(name, "k80s") == 0) {
\r
2114 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2116 return LoadBitmap(hinst, name);
\r
2120 /* Insert a color into the program's logical palette
\r
2121 structure. This code assumes the given color is
\r
2122 the result of the RGB or PALETTERGB macro, and it
\r
2123 knows how those macros work (which is documented).
\r
2126 InsertInPalette(COLORREF color)
\r
2128 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2130 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2131 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2132 pLogPal->palNumEntries--;
\r
2136 pe->peFlags = (char) 0;
\r
2137 pe->peRed = (char) (0xFF & color);
\r
2138 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2139 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2145 InitDrawingColors()
\r
2147 if (pLogPal == NULL) {
\r
2148 /* Allocate enough memory for a logical palette with
\r
2149 * PALETTESIZE entries and set the size and version fields
\r
2150 * of the logical palette structure.
\r
2152 pLogPal = (NPLOGPALETTE)
\r
2153 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2154 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2155 pLogPal->palVersion = 0x300;
\r
2157 pLogPal->palNumEntries = 0;
\r
2159 InsertInPalette(lightSquareColor);
\r
2160 InsertInPalette(darkSquareColor);
\r
2161 InsertInPalette(whitePieceColor);
\r
2162 InsertInPalette(blackPieceColor);
\r
2163 InsertInPalette(highlightSquareColor);
\r
2164 InsertInPalette(premoveHighlightColor);
\r
2166 /* create a logical color palette according the information
\r
2167 * in the LOGPALETTE structure.
\r
2169 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2171 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2172 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2173 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2174 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2175 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2176 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2177 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2178 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2179 /* [AS] Force rendering of the font-based pieces */
\r
2180 if( fontBitmapSquareSize > 0 ) {
\r
2181 fontBitmapSquareSize = 0;
\r
2187 BoardWidth(int boardSize, int n)
\r
2188 { /* [HGM] argument n added to allow different width and height */
\r
2189 int lineGap = sizeInfo[boardSize].lineGap;
\r
2191 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2192 lineGap = appData.overrideLineGap;
\r
2195 return (n + 1) * lineGap +
\r
2196 n * sizeInfo[boardSize].squareSize;
\r
2199 /* Respond to board resize by dragging edge */
\r
2201 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2203 BoardSize newSize = NUM_SIZES - 1;
\r
2204 static int recurse = 0;
\r
2205 if (IsIconic(hwndMain)) return;
\r
2206 if (recurse > 0) return;
\r
2208 while (newSize > 0) {
\r
2209 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2210 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2211 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2214 boardSize = newSize;
\r
2215 InitDrawingSizes(boardSize, flags);
\r
2220 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2223 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2225 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2226 ChessSquare piece;
\r
2227 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2229 SIZE clockSize, messageSize;
\r
2231 char buf[MSG_SIZ];
\r
2233 HMENU hmenu = GetMenu(hwndMain);
\r
2234 RECT crect, wrect, oldRect;
\r
2236 LOGBRUSH logbrush;
\r
2238 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2239 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2241 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2242 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2244 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2245 oldRect.top = wpMain.y;
\r
2246 oldRect.right = wpMain.x + wpMain.width;
\r
2247 oldRect.bottom = wpMain.y + wpMain.height;
\r
2249 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2250 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2251 squareSize = sizeInfo[boardSize].squareSize;
\r
2252 lineGap = sizeInfo[boardSize].lineGap;
\r
2253 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2255 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2256 lineGap = appData.overrideLineGap;
\r
2259 if (tinyLayout != oldTinyLayout) {
\r
2260 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2262 style &= ~WS_SYSMENU;
\r
2263 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2264 "&Minimize\tCtrl+F4");
\r
2266 style |= WS_SYSMENU;
\r
2267 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2269 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2271 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2272 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2273 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2275 DrawMenuBar(hwndMain);
\r
2278 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2279 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2281 /* Get text area sizes */
\r
2282 hdc = GetDC(hwndMain);
\r
2283 if (appData.clockMode) {
\r
2284 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2286 snprintf(buf, MSG_SIZ, _("White"));
\r
2288 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2289 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2290 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2291 str = _("We only care about the height here");
\r
2292 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2293 SelectObject(hdc, oldFont);
\r
2294 ReleaseDC(hwndMain, hdc);
\r
2296 /* Compute where everything goes */
\r
2297 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2298 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2299 logoHeight = 2*clockSize.cy;
\r
2300 leftLogoRect.left = OUTER_MARGIN;
\r
2301 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2302 leftLogoRect.top = OUTER_MARGIN;
\r
2303 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2305 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2306 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2307 rightLogoRect.top = OUTER_MARGIN;
\r
2308 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2311 whiteRect.left = leftLogoRect.right;
\r
2312 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2313 whiteRect.top = OUTER_MARGIN;
\r
2314 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2316 blackRect.right = rightLogoRect.left;
\r
2317 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2318 blackRect.top = whiteRect.top;
\r
2319 blackRect.bottom = whiteRect.bottom;
\r
2321 whiteRect.left = OUTER_MARGIN;
\r
2322 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2323 whiteRect.top = OUTER_MARGIN;
\r
2324 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2326 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2327 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2328 blackRect.top = whiteRect.top;
\r
2329 blackRect.bottom = whiteRect.bottom;
\r
2331 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2334 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2335 if (appData.showButtonBar) {
\r
2336 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2337 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2339 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2341 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2342 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2344 boardRect.left = OUTER_MARGIN;
\r
2345 boardRect.right = boardRect.left + boardWidth;
\r
2346 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2347 boardRect.bottom = boardRect.top + boardHeight;
\r
2349 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2350 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2351 oldBoardSize = boardSize;
\r
2352 oldTinyLayout = tinyLayout;
\r
2353 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2354 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2355 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2356 winW *= 1 + twoBoards;
\r
2357 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2358 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2359 wpMain.height = winH; // without disturbing window attachments
\r
2360 GetWindowRect(hwndMain, &wrect);
\r
2361 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2362 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2364 // [HGM] placement: let attached windows follow size change.
\r
2365 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2366 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2367 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2368 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2369 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2371 /* compensate if menu bar wrapped */
\r
2372 GetClientRect(hwndMain, &crect);
\r
2373 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2374 wpMain.height += offby;
\r
2376 case WMSZ_TOPLEFT:
\r
2377 SetWindowPos(hwndMain, NULL,
\r
2378 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2379 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2382 case WMSZ_TOPRIGHT:
\r
2384 SetWindowPos(hwndMain, NULL,
\r
2385 wrect.left, wrect.bottom - wpMain.height,
\r
2386 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2389 case WMSZ_BOTTOMLEFT:
\r
2391 SetWindowPos(hwndMain, NULL,
\r
2392 wrect.right - wpMain.width, wrect.top,
\r
2393 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2396 case WMSZ_BOTTOMRIGHT:
\r
2400 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2401 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2406 for (i = 0; i < N_BUTTONS; i++) {
\r
2407 if (buttonDesc[i].hwnd != NULL) {
\r
2408 DestroyWindow(buttonDesc[i].hwnd);
\r
2409 buttonDesc[i].hwnd = NULL;
\r
2411 if (appData.showButtonBar) {
\r
2412 buttonDesc[i].hwnd =
\r
2413 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2414 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2415 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2416 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2417 (HMENU) buttonDesc[i].id,
\r
2418 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2420 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2421 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2422 MAKELPARAM(FALSE, 0));
\r
2424 if (buttonDesc[i].id == IDM_Pause)
\r
2425 hwndPause = buttonDesc[i].hwnd;
\r
2426 buttonDesc[i].wndproc = (WNDPROC)
\r
2427 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2430 if (gridPen != NULL) DeleteObject(gridPen);
\r
2431 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2432 if (premovePen != NULL) DeleteObject(premovePen);
\r
2433 if (lineGap != 0) {
\r
2434 logbrush.lbStyle = BS_SOLID;
\r
2435 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2437 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2438 lineGap, &logbrush, 0, NULL);
\r
2439 logbrush.lbColor = highlightSquareColor;
\r
2441 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2442 lineGap, &logbrush, 0, NULL);
\r
2444 logbrush.lbColor = premoveHighlightColor;
\r
2446 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2447 lineGap, &logbrush, 0, NULL);
\r
2449 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2450 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2451 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2452 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2453 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2454 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2455 BOARD_WIDTH * (squareSize + lineGap);
\r
2456 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2458 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2459 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2460 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2461 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2462 lineGap / 2 + (i * (squareSize + lineGap));
\r
2463 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2464 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2465 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2469 /* [HGM] Licensing requirement */
\r
2471 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2474 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2476 GothicPopUp( "", VariantNormal);
\r
2479 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2481 /* Load piece bitmaps for this board size */
\r
2482 for (i=0; i<=2; i++) {
\r
2483 for (piece = WhitePawn;
\r
2484 (int) piece < (int) BlackPawn;
\r
2485 piece = (ChessSquare) ((int) piece + 1)) {
\r
2486 if (pieceBitmap[i][piece] != NULL)
\r
2487 DeleteObject(pieceBitmap[i][piece]);
\r
2491 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2492 // Orthodox Chess pieces
\r
2493 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2494 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2495 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2496 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2497 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2498 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2499 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2500 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2501 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2502 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2503 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2504 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2505 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2506 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2507 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2508 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2509 // in Shogi, Hijack the unused Queen for Lance
\r
2510 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2519 if(squareSize <= 72 && squareSize >= 33) {
\r
2520 /* A & C are available in most sizes now */
\r
2521 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2522 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2525 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2526 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2527 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2528 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2531 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2534 } else { // Smirf-like
\r
2535 if(gameInfo.variant == VariantSChess) {
\r
2536 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2540 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2541 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2542 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2545 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2546 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2547 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2548 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2549 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2550 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2553 } else { // WinBoard standard
\r
2554 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2561 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2562 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2583 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2586 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2589 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2590 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2591 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2593 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2594 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2597 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2598 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2599 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2600 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2601 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2602 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2603 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2607 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2608 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2609 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2610 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2611 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2612 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2613 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2614 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2615 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2616 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2617 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2618 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2621 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2622 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2623 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2624 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2625 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2626 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2627 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2628 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2629 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2630 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2631 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2632 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2633 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2634 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2635 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2639 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2640 /* special Shogi support in this size */
\r
2641 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2642 for (piece = WhitePawn;
\r
2643 (int) piece < (int) BlackPawn;
\r
2644 piece = (ChessSquare) ((int) piece + 1)) {
\r
2645 if (pieceBitmap[i][piece] != NULL)
\r
2646 DeleteObject(pieceBitmap[i][piece]);
\r
2649 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2650 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2651 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2652 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2653 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2654 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2655 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2656 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2657 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2658 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2659 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2660 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2661 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2662 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2663 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2664 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2665 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2666 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2667 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2668 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2669 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2670 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2671 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2672 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2673 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2674 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2675 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2676 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2677 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2678 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2679 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2680 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2681 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2682 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2683 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2684 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2685 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2686 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2687 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2688 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2689 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2690 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2696 PieceBitmap(ChessSquare p, int kind)
\r
2698 if ((int) p >= (int) BlackPawn)
\r
2699 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2701 return pieceBitmap[kind][(int) p];
\r
2704 /***************************************************************/
\r
2706 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2707 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2709 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2710 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2714 SquareToPos(int row, int column, int * x, int * y)
\r
2717 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2718 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2720 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2721 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2726 DrawCoordsOnDC(HDC hdc)
\r
2728 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2729 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2730 char str[2] = { NULLCHAR, NULLCHAR };
\r
2731 int oldMode, oldAlign, x, y, start, i;
\r
2735 if (!appData.showCoords)
\r
2738 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2740 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2741 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2742 oldAlign = GetTextAlign(hdc);
\r
2743 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2745 y = boardRect.top + lineGap;
\r
2746 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2748 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2749 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2750 str[0] = files[start + i];
\r
2751 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2752 y += squareSize + lineGap;
\r
2755 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2757 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2758 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2759 str[0] = ranks[start + i];
\r
2760 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2761 x += squareSize + lineGap;
\r
2764 SelectObject(hdc, oldBrush);
\r
2765 SetBkMode(hdc, oldMode);
\r
2766 SetTextAlign(hdc, oldAlign);
\r
2767 SelectObject(hdc, oldFont);
\r
2771 DrawGridOnDC(HDC hdc)
\r
2775 if (lineGap != 0) {
\r
2776 oldPen = SelectObject(hdc, gridPen);
\r
2777 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2778 SelectObject(hdc, oldPen);
\r
2782 #define HIGHLIGHT_PEN 0
\r
2783 #define PREMOVE_PEN 1
\r
2786 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2789 HPEN oldPen, hPen;
\r
2790 if (lineGap == 0) return;
\r
2792 x1 = boardRect.left +
\r
2793 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2794 y1 = boardRect.top +
\r
2795 lineGap/2 + y * (squareSize + lineGap);
\r
2797 x1 = boardRect.left +
\r
2798 lineGap/2 + x * (squareSize + lineGap);
\r
2799 y1 = boardRect.top +
\r
2800 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2802 hPen = pen ? premovePen : highlightPen;
\r
2803 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2804 MoveToEx(hdc, x1, y1, NULL);
\r
2805 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2806 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2807 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2808 LineTo(hdc, x1, y1);
\r
2809 SelectObject(hdc, oldPen);
\r
2813 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2816 for (i=0; i<2; i++) {
\r
2817 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2818 DrawHighlightOnDC(hdc, TRUE,
\r
2819 h->sq[i].x, h->sq[i].y,
\r
2824 /* Note: sqcolor is used only in monoMode */
\r
2825 /* Note that this code is largely duplicated in woptions.c,
\r
2826 function DrawSampleSquare, so that needs to be updated too */
\r
2828 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2830 HBITMAP oldBitmap;
\r
2834 if (appData.blindfold) return;
\r
2836 /* [AS] Use font-based pieces if needed */
\r
2837 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2838 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2839 CreatePiecesFromFont();
\r
2841 if( fontBitmapSquareSize == squareSize ) {
\r
2842 int index = TranslatePieceToFontPiece(piece);
\r
2844 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2846 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2847 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2851 squareSize, squareSize,
\r
2856 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2858 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2859 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2863 squareSize, squareSize,
\r
2872 if (appData.monoMode) {
\r
2873 SelectObject(tmphdc, PieceBitmap(piece,
\r
2874 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2875 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2876 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2878 tmpSize = squareSize;
\r
2880 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2881 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2882 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2883 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2884 x += (squareSize - minorSize)>>1;
\r
2885 y += squareSize - minorSize - 2;
\r
2886 tmpSize = minorSize;
\r
2888 if (color || appData.allWhite ) {
\r
2889 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2891 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2892 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2893 if(appData.upsideDown && color==flipView)
\r
2894 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2896 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2897 /* Use black for outline of white pieces */
\r
2898 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2899 if(appData.upsideDown && color==flipView)
\r
2900 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2902 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2904 /* Use square color for details of black pieces */
\r
2905 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2906 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2907 if(appData.upsideDown && !flipView)
\r
2908 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2910 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2912 SelectObject(hdc, oldBrush);
\r
2913 SelectObject(tmphdc, oldBitmap);
\r
2917 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2918 int GetBackTextureMode( int algo )
\r
2920 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2924 case BACK_TEXTURE_MODE_PLAIN:
\r
2925 result = 1; /* Always use identity map */
\r
2927 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2928 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2936 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2937 to handle redraws cleanly (as random numbers would always be different).
\r
2939 VOID RebuildTextureSquareInfo()
\r
2949 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2951 if( liteBackTexture != NULL ) {
\r
2952 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2953 lite_w = bi.bmWidth;
\r
2954 lite_h = bi.bmHeight;
\r
2958 if( darkBackTexture != NULL ) {
\r
2959 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2960 dark_w = bi.bmWidth;
\r
2961 dark_h = bi.bmHeight;
\r
2965 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2966 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2967 if( (col + row) & 1 ) {
\r
2969 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2970 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2971 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2973 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2974 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2975 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2977 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2978 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2983 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2984 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2985 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2987 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2988 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2989 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2991 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2992 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2999 /* [AS] Arrow highlighting support */
\r
3001 static double A_WIDTH = 5; /* Width of arrow body */
\r
3003 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3004 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3006 static double Sqr( double x )
\r
3011 static int Round( double x )
\r
3013 return (int) (x + 0.5);
\r
3016 /* Draw an arrow between two points using current settings */
\r
3017 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3020 double dx, dy, j, k, x, y;
\r
3022 if( d_x == s_x ) {
\r
3023 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3025 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3028 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3029 arrow[1].y = d_y - h;
\r
3031 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3032 arrow[2].y = d_y - h;
\r
3037 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3038 arrow[5].y = d_y - h;
\r
3040 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3041 arrow[4].y = d_y - h;
\r
3043 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3046 else if( d_y == s_y ) {
\r
3047 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3050 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3052 arrow[1].x = d_x - w;
\r
3053 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3055 arrow[2].x = d_x - w;
\r
3056 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3061 arrow[5].x = d_x - w;
\r
3062 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3064 arrow[4].x = d_x - w;
\r
3065 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3068 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3071 /* [AS] Needed a lot of paper for this! :-) */
\r
3072 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3073 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3075 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3077 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3082 arrow[0].x = Round(x - j);
\r
3083 arrow[0].y = Round(y + j*dx);
\r
3085 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3086 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3089 x = (double) d_x - k;
\r
3090 y = (double) d_y - k*dy;
\r
3093 x = (double) d_x + k;
\r
3094 y = (double) d_y + k*dy;
\r
3097 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3099 arrow[6].x = Round(x - j);
\r
3100 arrow[6].y = Round(y + j*dx);
\r
3102 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3103 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3105 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3106 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3111 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3112 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3115 Polygon( hdc, arrow, 7 );
\r
3118 /* [AS] Draw an arrow between two squares */
\r
3119 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3121 int s_x, s_y, d_x, d_y;
\r
3128 if( s_col == d_col && s_row == d_row ) {
\r
3132 /* Get source and destination points */
\r
3133 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3134 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3137 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3139 else if( d_y < s_y ) {
\r
3140 d_y += squareSize / 2 + squareSize / 4;
\r
3143 d_y += squareSize / 2;
\r
3147 d_x += squareSize / 2 - squareSize / 4;
\r
3149 else if( d_x < s_x ) {
\r
3150 d_x += squareSize / 2 + squareSize / 4;
\r
3153 d_x += squareSize / 2;
\r
3156 s_x += squareSize / 2;
\r
3157 s_y += squareSize / 2;
\r
3159 /* Adjust width */
\r
3160 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3163 stLB.lbStyle = BS_SOLID;
\r
3164 stLB.lbColor = appData.highlightArrowColor;
\r
3167 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3168 holdpen = SelectObject( hdc, hpen );
\r
3169 hbrush = CreateBrushIndirect( &stLB );
\r
3170 holdbrush = SelectObject( hdc, hbrush );
\r
3172 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3174 SelectObject( hdc, holdpen );
\r
3175 SelectObject( hdc, holdbrush );
\r
3176 DeleteObject( hpen );
\r
3177 DeleteObject( hbrush );
\r
3180 BOOL HasHighlightInfo()
\r
3182 BOOL result = FALSE;
\r
3184 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3185 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3193 BOOL IsDrawArrowEnabled()
\r
3195 BOOL result = FALSE;
\r
3197 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3204 VOID DrawArrowHighlight( HDC hdc )
\r
3206 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3207 DrawArrowBetweenSquares( hdc,
\r
3208 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3209 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3213 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3215 HRGN result = NULL;
\r
3217 if( HasHighlightInfo() ) {
\r
3218 int x1, y1, x2, y2;
\r
3219 int sx, sy, dx, dy;
\r
3221 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3222 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3224 sx = MIN( x1, x2 );
\r
3225 sy = MIN( y1, y2 );
\r
3226 dx = MAX( x1, x2 ) + squareSize;
\r
3227 dy = MAX( y1, y2 ) + squareSize;
\r
3229 result = CreateRectRgn( sx, sy, dx, dy );
\r
3236 Warning: this function modifies the behavior of several other functions.
\r
3238 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3239 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3240 repaint is scattered all over the place, which is not good for features such as
\r
3241 "arrow highlighting" that require a full repaint of the board.
\r
3243 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3244 user interaction, when speed is not so important) but especially to avoid errors
\r
3245 in the displayed graphics.
\r
3247 In such patched places, I always try refer to this function so there is a single
\r
3248 place to maintain knowledge.
\r
3250 To restore the original behavior, just return FALSE unconditionally.
\r
3252 BOOL IsFullRepaintPreferrable()
\r
3254 BOOL result = FALSE;
\r
3256 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3257 /* Arrow may appear on the board */
\r
3265 This function is called by DrawPosition to know whether a full repaint must
\r
3268 Only DrawPosition may directly call this function, which makes use of
\r
3269 some state information. Other function should call DrawPosition specifying
\r
3270 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3272 BOOL DrawPositionNeedsFullRepaint()
\r
3274 BOOL result = FALSE;
\r
3277 Probably a slightly better policy would be to trigger a full repaint
\r
3278 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3279 but animation is fast enough that it's difficult to notice.
\r
3281 if( animInfo.piece == EmptySquare ) {
\r
3282 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3291 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3293 int row, column, x, y, square_color, piece_color;
\r
3294 ChessSquare piece;
\r
3296 HDC texture_hdc = NULL;
\r
3298 /* [AS] Initialize background textures if needed */
\r
3299 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3300 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3301 if( backTextureSquareSize != squareSize
\r
3302 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3303 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3304 backTextureSquareSize = squareSize;
\r
3305 RebuildTextureSquareInfo();
\r
3308 texture_hdc = CreateCompatibleDC( hdc );
\r
3311 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3312 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3314 SquareToPos(row, column, &x, &y);
\r
3316 piece = board[row][column];
\r
3318 square_color = ((column + row) % 2) == 1;
\r
3319 if( gameInfo.variant == VariantXiangqi ) {
\r
3320 square_color = !InPalace(row, column);
\r
3321 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3322 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3324 piece_color = (int) piece < (int) BlackPawn;
\r
3327 /* [HGM] holdings file: light square or black */
\r
3328 if(column == BOARD_LEFT-2) {
\r
3329 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3332 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3336 if(column == BOARD_RGHT + 1 ) {
\r
3337 if( row < gameInfo.holdingsSize )
\r
3340 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3344 if(column == BOARD_LEFT-1 ) /* left align */
\r
3345 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3346 else if( column == BOARD_RGHT) /* right align */
\r
3347 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3349 if (appData.monoMode) {
\r
3350 if (piece == EmptySquare) {
\r
3351 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3352 square_color ? WHITENESS : BLACKNESS);
\r
3354 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3357 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3358 /* [AS] Draw the square using a texture bitmap */
\r
3359 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3360 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3361 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3364 squareSize, squareSize,
\r
3367 backTextureSquareInfo[r][c].mode,
\r
3368 backTextureSquareInfo[r][c].x,
\r
3369 backTextureSquareInfo[r][c].y );
\r
3371 SelectObject( texture_hdc, hbm );
\r
3373 if (piece != EmptySquare) {
\r
3374 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3378 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3380 oldBrush = SelectObject(hdc, brush );
\r
3381 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3382 SelectObject(hdc, oldBrush);
\r
3383 if (piece != EmptySquare)
\r
3384 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3389 if( texture_hdc != NULL ) {
\r
3390 DeleteDC( texture_hdc );
\r
3394 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3395 void fputDW(FILE *f, int x)
\r
3397 fputc(x & 255, f);
\r
3398 fputc(x>>8 & 255, f);
\r
3399 fputc(x>>16 & 255, f);
\r
3400 fputc(x>>24 & 255, f);
\r
3403 #define MAX_CLIPS 200 /* more than enough */
\r
3406 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3408 // HBITMAP bufferBitmap;
\r
3413 int w = 100, h = 50;
\r
3415 if(logo == NULL) {
\r
3416 if(!logoHeight) return;
\r
3417 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3419 // GetClientRect(hwndMain, &Rect);
\r
3420 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3421 // Rect.bottom-Rect.top+1);
\r
3422 tmphdc = CreateCompatibleDC(hdc);
\r
3423 hbm = SelectObject(tmphdc, logo);
\r
3424 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3428 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3429 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3430 SelectObject(tmphdc, hbm);
\r
3438 HDC hdc = GetDC(hwndMain);
\r
3439 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3440 if(appData.autoLogo) {
\r
3442 switch(gameMode) { // pick logos based on game mode
\r
3443 case IcsObserving:
\r
3444 whiteLogo = second.programLogo; // ICS logo
\r
3445 blackLogo = second.programLogo;
\r
3448 case IcsPlayingWhite:
\r
3449 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3450 blackLogo = second.programLogo; // ICS logo
\r
3452 case IcsPlayingBlack:
\r
3453 whiteLogo = second.programLogo; // ICS logo
\r
3454 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3456 case TwoMachinesPlay:
\r
3457 if(first.twoMachinesColor[0] == 'b') {
\r
3458 whiteLogo = second.programLogo;
\r
3459 blackLogo = first.programLogo;
\r
3462 case MachinePlaysWhite:
\r
3463 blackLogo = userLogo;
\r
3465 case MachinePlaysBlack:
\r
3466 whiteLogo = userLogo;
\r
3467 blackLogo = first.programLogo;
\r
3470 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3471 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3472 ReleaseDC(hwndMain, hdc);
\r
3477 UpdateLogos(int display)
\r
3478 { // called after loading new engine(s), in tourney or from menu
\r
3479 LoadLogo(&first, 0, FALSE);
\r
3480 LoadLogo(&second, 1, appData.icsActive);
\r
3481 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3482 if(display) DisplayLogos();
\r
3485 static HDC hdcSeek;
\r
3487 // [HGM] seekgraph
\r
3488 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3491 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3492 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3493 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3494 SelectObject( hdcSeek, hp );
\r
3497 // front-end wrapper for drawing functions to do rectangles
\r
3498 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3503 if (hdcSeek == NULL) {
\r
3504 hdcSeek = GetDC(hwndMain);
\r
3505 if (!appData.monoMode) {
\r
3506 SelectPalette(hdcSeek, hPal, FALSE);
\r
3507 RealizePalette(hdcSeek);
\r
3510 hp = SelectObject( hdcSeek, gridPen );
\r
3511 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3512 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3513 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3514 SelectObject( hdcSeek, hp );
\r
3517 // front-end wrapper for putting text in graph
\r
3518 void DrawSeekText(char *buf, int x, int y)
\r
3521 SetBkMode( hdcSeek, TRANSPARENT );
\r
3522 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3523 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3526 void DrawSeekDot(int x, int y, int color)
\r
3528 int square = color & 0x80;
\r
3529 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3530 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3533 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3534 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3536 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3537 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3538 SelectObject(hdcSeek, oldBrush);
\r
3542 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3544 static Board lastReq[2], lastDrawn[2];
\r
3545 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3546 static int lastDrawnFlipView = 0;
\r
3547 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3548 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3551 HBITMAP bufferBitmap;
\r
3552 HBITMAP oldBitmap;
\r
3554 HRGN clips[MAX_CLIPS];
\r
3555 ChessSquare dragged_piece = EmptySquare;
\r
3556 int nr = twoBoards*partnerUp;
\r
3558 /* I'm undecided on this - this function figures out whether a full
\r
3559 * repaint is necessary on its own, so there's no real reason to have the
\r
3560 * caller tell it that. I think this can safely be set to FALSE - but
\r
3561 * if we trust the callers not to request full repaints unnessesarily, then
\r
3562 * we could skip some clipping work. In other words, only request a full
\r
3563 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3564 * gamestart and similar) --Hawk
\r
3566 Boolean fullrepaint = repaint;
\r
3568 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3570 if( DrawPositionNeedsFullRepaint() ) {
\r
3571 fullrepaint = TRUE;
\r
3574 if (board == NULL) {
\r
3575 if (!lastReqValid[nr]) {
\r
3578 board = lastReq[nr];
\r
3580 CopyBoard(lastReq[nr], board);
\r
3581 lastReqValid[nr] = 1;
\r
3584 if (doingSizing) {
\r
3588 if (IsIconic(hwndMain)) {
\r
3592 if (hdc == NULL) {
\r
3593 hdc = GetDC(hwndMain);
\r
3594 if (!appData.monoMode) {
\r
3595 SelectPalette(hdc, hPal, FALSE);
\r
3596 RealizePalette(hdc);
\r
3600 releaseDC = FALSE;
\r
3603 /* Create some work-DCs */
\r
3604 hdcmem = CreateCompatibleDC(hdc);
\r
3605 tmphdc = CreateCompatibleDC(hdc);
\r
3607 /* If dragging is in progress, we temporarely remove the piece */
\r
3608 /* [HGM] or temporarily decrease count if stacked */
\r
3609 /* !! Moved to before board compare !! */
\r
3610 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3611 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3612 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3613 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3614 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3616 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3617 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3618 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3620 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3623 /* Figure out which squares need updating by comparing the
\r
3624 * newest board with the last drawn board and checking if
\r
3625 * flipping has changed.
\r
3627 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3628 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3629 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3630 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3631 SquareToPos(row, column, &x, &y);
\r
3632 clips[num_clips++] =
\r
3633 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3637 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3638 for (i=0; i<2; i++) {
\r
3639 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3640 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3641 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3642 lastDrawnHighlight.sq[i].y >= 0) {
\r
3643 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3644 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3645 clips[num_clips++] =
\r
3646 CreateRectRgn(x - lineGap, y - lineGap,
\r
3647 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3649 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3650 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3651 clips[num_clips++] =
\r
3652 CreateRectRgn(x - lineGap, y - lineGap,
\r
3653 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3657 for (i=0; i<2; i++) {
\r
3658 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3659 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3660 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3661 lastDrawnPremove.sq[i].y >= 0) {
\r
3662 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3663 lastDrawnPremove.sq[i].x, &x, &y);
\r
3664 clips[num_clips++] =
\r
3665 CreateRectRgn(x - lineGap, y - lineGap,
\r
3666 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3668 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3669 premoveHighlightInfo.sq[i].y >= 0) {
\r
3670 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3671 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3672 clips[num_clips++] =
\r
3673 CreateRectRgn(x - lineGap, y - lineGap,
\r
3674 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3678 } else { // nr == 1
\r
3679 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3680 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3681 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3682 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3683 for (i=0; i<2; i++) {
\r
3684 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3685 partnerHighlightInfo.sq[i].y >= 0) {
\r
3686 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3687 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3688 clips[num_clips++] =
\r
3689 CreateRectRgn(x - lineGap, y - lineGap,
\r
3690 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3692 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3693 oldPartnerHighlight.sq[i].y >= 0) {
\r
3694 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3695 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3696 clips[num_clips++] =
\r
3697 CreateRectRgn(x - lineGap, y - lineGap,
\r
3698 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3703 fullrepaint = TRUE;
\r
3706 /* Create a buffer bitmap - this is the actual bitmap
\r
3707 * being written to. When all the work is done, we can
\r
3708 * copy it to the real DC (the screen). This avoids
\r
3709 * the problems with flickering.
\r
3711 GetClientRect(hwndMain, &Rect);
\r
3712 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3713 Rect.bottom-Rect.top+1);
\r
3714 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3715 if (!appData.monoMode) {
\r
3716 SelectPalette(hdcmem, hPal, FALSE);
\r
3719 /* Create clips for dragging */
\r
3720 if (!fullrepaint) {
\r
3721 if (dragInfo.from.x >= 0) {
\r
3722 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3723 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3725 if (dragInfo.start.x >= 0) {
\r
3726 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3727 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3729 if (dragInfo.pos.x >= 0) {
\r
3730 x = dragInfo.pos.x - squareSize / 2;
\r
3731 y = dragInfo.pos.y - squareSize / 2;
\r
3732 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3734 if (dragInfo.lastpos.x >= 0) {
\r
3735 x = dragInfo.lastpos.x - squareSize / 2;
\r
3736 y = dragInfo.lastpos.y - squareSize / 2;
\r
3737 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3741 /* Are we animating a move?
\r
3743 * - remove the piece from the board (temporarely)
\r
3744 * - calculate the clipping region
\r
3746 if (!fullrepaint) {
\r
3747 if (animInfo.piece != EmptySquare) {
\r
3748 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3749 x = boardRect.left + animInfo.lastpos.x;
\r
3750 y = boardRect.top + animInfo.lastpos.y;
\r
3751 x2 = boardRect.left + animInfo.pos.x;
\r
3752 y2 = boardRect.top + animInfo.pos.y;
\r
3753 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3754 /* Slight kludge. The real problem is that after AnimateMove is
\r
3755 done, the position on the screen does not match lastDrawn.
\r
3756 This currently causes trouble only on e.p. captures in
\r
3757 atomic, where the piece moves to an empty square and then
\r
3758 explodes. The old and new positions both had an empty square
\r
3759 at the destination, but animation has drawn a piece there and
\r
3760 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3761 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3765 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3766 if (num_clips == 0)
\r
3767 fullrepaint = TRUE;
\r
3769 /* Set clipping on the memory DC */
\r
3770 if (!fullrepaint) {
\r
3771 SelectClipRgn(hdcmem, clips[0]);
\r
3772 for (x = 1; x < num_clips; x++) {
\r
3773 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3774 abort(); // this should never ever happen!
\r
3778 /* Do all the drawing to the memory DC */
\r
3779 if(explodeInfo.radius) { // [HGM] atomic
\r
3781 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3782 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3783 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3784 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3785 x += squareSize/2;
\r
3786 y += squareSize/2;
\r
3787 if(!fullrepaint) {
\r
3788 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3789 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3791 DrawGridOnDC(hdcmem);
\r
3792 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3793 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3794 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3795 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3796 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3797 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3798 SelectObject(hdcmem, oldBrush);
\r
3800 DrawGridOnDC(hdcmem);
\r
3801 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3802 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3803 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3805 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3806 oldPartnerHighlight = partnerHighlightInfo;
\r
3808 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3810 if(nr == 0) // [HGM] dual: markers only on left board
\r
3811 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3812 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3813 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3814 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3815 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3816 SquareToPos(row, column, &x, &y);
\r
3817 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3818 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3819 SelectObject(hdcmem, oldBrush);
\r
3824 if( appData.highlightMoveWithArrow ) {
\r
3825 DrawArrowHighlight(hdcmem);
\r
3828 DrawCoordsOnDC(hdcmem);
\r
3830 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3831 /* to make sure lastDrawn contains what is actually drawn */
\r
3833 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3834 if (dragged_piece != EmptySquare) {
\r
3835 /* [HGM] or restack */
\r
3836 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3837 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3839 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3840 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3841 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3842 x = dragInfo.pos.x - squareSize / 2;
\r
3843 y = dragInfo.pos.y - squareSize / 2;
\r
3844 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3845 ((int) dragInfo.piece < (int) BlackPawn),
\r
3846 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3849 /* Put the animated piece back into place and draw it */
\r
3850 if (animInfo.piece != EmptySquare) {
\r
3851 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3852 x = boardRect.left + animInfo.pos.x;
\r
3853 y = boardRect.top + animInfo.pos.y;
\r
3854 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3855 ((int) animInfo.piece < (int) BlackPawn),
\r
3856 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3859 /* Release the bufferBitmap by selecting in the old bitmap
\r
3860 * and delete the memory DC
\r
3862 SelectObject(hdcmem, oldBitmap);
\r
3865 /* Set clipping on the target DC */
\r
3866 if (!fullrepaint) {
\r
3867 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3869 GetRgnBox(clips[x], &rect);
\r
3870 DeleteObject(clips[x]);
\r
3871 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3872 rect.right + wpMain.width/2, rect.bottom);
\r
3874 SelectClipRgn(hdc, clips[0]);
\r
3875 for (x = 1; x < num_clips; x++) {
\r
3876 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3877 abort(); // this should never ever happen!
\r
3881 /* Copy the new bitmap onto the screen in one go.
\r
3882 * This way we avoid any flickering
\r
3884 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3885 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3886 boardRect.right - boardRect.left,
\r
3887 boardRect.bottom - boardRect.top,
\r
3888 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3889 if(saveDiagFlag) {
\r
3890 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3891 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3893 GetObject(bufferBitmap, sizeof(b), &b);
\r
3894 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3895 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3896 bih.biWidth = b.bmWidth;
\r
3897 bih.biHeight = b.bmHeight;
\r
3899 bih.biBitCount = b.bmBitsPixel;
\r
3900 bih.biCompression = 0;
\r
3901 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3902 bih.biXPelsPerMeter = 0;
\r
3903 bih.biYPelsPerMeter = 0;
\r
3904 bih.biClrUsed = 0;
\r
3905 bih.biClrImportant = 0;
\r
3906 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3907 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3908 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3909 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3911 wb = b.bmWidthBytes;
\r
3913 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3914 int k = ((int*) pData)[i];
\r
3915 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3916 if(j >= 16) break;
\r
3918 if(j >= nrColors) nrColors = j+1;
\r
3920 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3922 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3923 for(w=0; w<(wb>>2); w+=2) {
\r
3924 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3925 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3926 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3927 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3928 pData[p++] = m | j<<4;
\r
3930 while(p&3) pData[p++] = 0;
\r
3933 wb = ((wb+31)>>5)<<2;
\r
3935 // write BITMAPFILEHEADER
\r
3936 fprintf(diagFile, "BM");
\r
3937 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3938 fputDW(diagFile, 0);
\r
3939 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3940 // write BITMAPINFOHEADER
\r
3941 fputDW(diagFile, 40);
\r
3942 fputDW(diagFile, b.bmWidth);
\r
3943 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3944 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3945 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\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 fputDW(diagFile, 0);
\r
3952 // write color table
\r
3954 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3955 // write bitmap data
\r
3956 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3957 fputc(pData[i], diagFile);
\r
3962 SelectObject(tmphdc, oldBitmap);
\r
3964 /* Massive cleanup */
\r
3965 for (x = 0; x < num_clips; x++)
\r
3966 DeleteObject(clips[x]);
\r
3969 DeleteObject(bufferBitmap);
\r
3972 ReleaseDC(hwndMain, hdc);
\r
3974 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3976 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3978 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3981 /* CopyBoard(lastDrawn, board);*/
\r
3982 lastDrawnHighlight = highlightInfo;
\r
3983 lastDrawnPremove = premoveHighlightInfo;
\r
3984 lastDrawnFlipView = flipView;
\r
3985 lastDrawnValid[nr] = 1;
\r
3988 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3993 saveDiagFlag = 1; diagFile = f;
\r
3994 HDCDrawPosition(NULL, TRUE, NULL);
\r
4002 /*---------------------------------------------------------------------------*\
\r
4003 | CLIENT PAINT PROCEDURE
\r
4004 | This is the main event-handler for the WM_PAINT message.
\r
4006 \*---------------------------------------------------------------------------*/
\r
4008 PaintProc(HWND hwnd)
\r
4014 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4015 if (IsIconic(hwnd)) {
\r
4016 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4018 if (!appData.monoMode) {
\r
4019 SelectPalette(hdc, hPal, FALSE);
\r
4020 RealizePalette(hdc);
\r
4022 HDCDrawPosition(hdc, 1, NULL);
\r
4023 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4024 flipView = !flipView; partnerUp = !partnerUp;
\r
4025 HDCDrawPosition(hdc, 1, NULL);
\r
4026 flipView = !flipView; partnerUp = !partnerUp;
\r
4029 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4030 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4031 ETO_CLIPPED|ETO_OPAQUE,
\r
4032 &messageRect, messageText, strlen(messageText), NULL);
\r
4033 SelectObject(hdc, oldFont);
\r
4034 DisplayBothClocks();
\r
4037 EndPaint(hwnd,&ps);
\r
4045 * If the user selects on a border boundary, return -1; if off the board,
\r
4046 * return -2. Otherwise map the event coordinate to the square.
\r
4047 * The offset boardRect.left or boardRect.top must already have been
\r
4048 * subtracted from x.
\r
4050 int EventToSquare(x, limit)
\r
4058 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4060 x /= (squareSize + lineGap);
\r
4072 DropEnable dropEnables[] = {
\r
4073 { 'P', DP_Pawn, N_("Pawn") },
\r
4074 { 'N', DP_Knight, N_("Knight") },
\r
4075 { 'B', DP_Bishop, N_("Bishop") },
\r
4076 { 'R', DP_Rook, N_("Rook") },
\r
4077 { 'Q', DP_Queen, N_("Queen") },
\r
4081 SetupDropMenu(HMENU hmenu)
\r
4083 int i, count, enable;
\r
4085 extern char white_holding[], black_holding[];
\r
4086 char item[MSG_SIZ];
\r
4088 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4089 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4090 dropEnables[i].piece);
\r
4092 while (p && *p++ == dropEnables[i].piece) count++;
\r
4093 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4094 enable = count > 0 || !appData.testLegality
\r
4095 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4096 && !appData.icsActive);
\r
4097 ModifyMenu(hmenu, dropEnables[i].command,
\r
4098 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4099 dropEnables[i].command, item);
\r
4103 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4105 dragInfo.lastpos.x = boardRect.left + x;
\r
4106 dragInfo.lastpos.y = boardRect.top + y;
\r
4107 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4108 dragInfo.from.x = fromX;
\r
4109 dragInfo.from.y = fromY;
\r
4110 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4111 dragInfo.start = dragInfo.from;
\r
4112 SetCapture(hwndMain);
\r
4115 void DragPieceEnd(int x, int y)
\r
4118 dragInfo.start.x = dragInfo.start.y = -1;
\r
4119 dragInfo.from = dragInfo.start;
\r
4120 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4123 void ChangeDragPiece(ChessSquare piece)
\r
4125 dragInfo.piece = piece;
\r
4128 /* Event handler for mouse messages */
\r
4130 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4134 static int recursive = 0;
\r
4136 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4139 if (message == WM_MBUTTONUP) {
\r
4140 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4141 to the middle button: we simulate pressing the left button too!
\r
4143 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4144 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4150 pt.x = LOWORD(lParam);
\r
4151 pt.y = HIWORD(lParam);
\r
4152 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4153 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4154 if (!flipView && y >= 0) {
\r
4155 y = BOARD_HEIGHT - 1 - y;
\r
4157 if (flipView && x >= 0) {
\r
4158 x = BOARD_WIDTH - 1 - x;
\r
4161 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4163 switch (message) {
\r
4164 case WM_LBUTTONDOWN:
\r
4165 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4166 ClockClick(flipClock); break;
\r
4167 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4168 ClockClick(!flipClock); break;
\r
4170 dragInfo.start.x = dragInfo.start.y = -1;
\r
4171 dragInfo.from = dragInfo.start;
\r
4172 if(fromX == -1 && frozen) { // not sure where this is for
\r
4173 fromX = fromY = -1;
\r
4174 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4177 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4178 DrawPosition(TRUE, NULL);
\r
4181 case WM_LBUTTONUP:
\r
4182 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4183 DrawPosition(TRUE, NULL);
\r
4186 case WM_MOUSEMOVE:
\r
4187 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4188 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4189 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4190 if ((appData.animateDragging || appData.highlightDragging)
\r
4191 && (wParam & MK_LBUTTON)
\r
4192 && dragInfo.from.x >= 0)
\r
4194 BOOL full_repaint = FALSE;
\r
4196 if (appData.animateDragging) {
\r
4197 dragInfo.pos = pt;
\r
4199 if (appData.highlightDragging) {
\r
4200 SetHighlights(fromX, fromY, x, y);
\r
4201 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4202 full_repaint = TRUE;
\r
4206 DrawPosition( full_repaint, NULL);
\r
4208 dragInfo.lastpos = dragInfo.pos;
\r
4212 case WM_MOUSEWHEEL: // [DM]
\r
4213 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4214 /* Mouse Wheel is being rolled forward
\r
4215 * Play moves forward
\r
4217 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4218 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4219 /* Mouse Wheel is being rolled backward
\r
4220 * Play moves backward
\r
4222 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4223 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4227 case WM_MBUTTONUP:
\r
4228 case WM_RBUTTONUP:
\r
4230 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4233 case WM_MBUTTONDOWN:
\r
4234 case WM_RBUTTONDOWN:
\r
4237 fromX = fromY = -1;
\r
4238 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4239 dragInfo.start.x = dragInfo.start.y = -1;
\r
4240 dragInfo.from = dragInfo.start;
\r
4241 dragInfo.lastpos = dragInfo.pos;
\r
4242 if (appData.highlightDragging) {
\r
4243 ClearHighlights();
\r
4246 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4247 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4248 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4249 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4250 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4254 DrawPosition(TRUE, NULL);
\r
4256 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4259 if (message == WM_MBUTTONDOWN) {
\r
4260 buttonCount = 3; /* even if system didn't think so */
\r
4261 if (wParam & MK_SHIFT)
\r
4262 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4264 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4265 } else { /* message == WM_RBUTTONDOWN */
\r
4266 /* Just have one menu, on the right button. Windows users don't
\r
4267 think to try the middle one, and sometimes other software steals
\r
4268 it, or it doesn't really exist. */
\r
4269 if(gameInfo.variant != VariantShogi)
\r
4270 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4272 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4276 SetCapture(hwndMain);
\r
4279 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4280 SetupDropMenu(hmenu);
\r
4281 MenuPopup(hwnd, pt, hmenu, -1);
\r
4291 /* Preprocess messages for buttons in main window */
\r
4293 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4295 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4298 for (i=0; i<N_BUTTONS; i++) {
\r
4299 if (buttonDesc[i].id == id) break;
\r
4301 if (i == N_BUTTONS) return 0;
\r
4302 switch (message) {
\r
4307 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4308 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4315 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4318 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4319 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4320 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4321 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4323 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4325 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4326 TypeInEvent((char)wParam);
\r
4332 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4335 /* Process messages for Promotion dialog box */
\r
4337 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4341 switch (message) {
\r
4342 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4343 /* Center the dialog over the application window */
\r
4344 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4345 Translate(hDlg, DLG_PromotionKing);
\r
4346 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4347 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4348 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4349 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4350 SW_SHOW : SW_HIDE);
\r
4351 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4352 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4353 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4354 PieceToChar(WhiteAngel) != '~') ||
\r
4355 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4356 PieceToChar(BlackAngel) != '~') ) ?
\r
4357 SW_SHOW : SW_HIDE);
\r
4358 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4359 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4360 PieceToChar(WhiteMarshall) != '~') ||
\r
4361 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4362 PieceToChar(BlackMarshall) != '~') ) ?
\r
4363 SW_SHOW : SW_HIDE);
\r
4364 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4365 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4366 gameInfo.variant != VariantShogi ?
\r
4367 SW_SHOW : SW_HIDE);
\r
4368 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4369 gameInfo.variant != VariantShogi ?
\r
4370 SW_SHOW : SW_HIDE);
\r
4371 if(gameInfo.variant == VariantShogi) {
\r
4372 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4373 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4374 SetWindowText(hDlg, "Promote?");
\r
4376 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4377 gameInfo.variant == VariantSuper ?
\r
4378 SW_SHOW : SW_HIDE);
\r
4381 case WM_COMMAND: /* message: received a command */
\r
4382 switch (LOWORD(wParam)) {
\r
4384 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4385 ClearHighlights();
\r
4386 DrawPosition(FALSE, NULL);
\r
4389 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4392 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4395 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4396 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4399 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4400 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4402 case PB_Chancellor:
\r
4403 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4405 case PB_Archbishop:
\r
4406 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4409 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4414 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4415 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4416 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4417 fromX = fromY = -1;
\r
4418 if (!appData.highlightLastMove) {
\r
4419 ClearHighlights();
\r
4420 DrawPosition(FALSE, NULL);
\r
4427 /* Pop up promotion dialog */
\r
4429 PromotionPopup(HWND hwnd)
\r
4433 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4434 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4435 hwnd, (DLGPROC)lpProc);
\r
4436 FreeProcInstance(lpProc);
\r
4442 DrawPosition(TRUE, NULL);
\r
4443 PromotionPopup(hwndMain);
\r
4446 /* Toggle ShowThinking */
\r
4448 ToggleShowThinking()
\r
4450 appData.showThinking = !appData.showThinking;
\r
4451 ShowThinkingEvent();
\r
4455 LoadGameDialog(HWND hwnd, char* title)
\r
4459 char fileTitle[MSG_SIZ];
\r
4460 f = OpenFileDialog(hwnd, "rb", "",
\r
4461 appData.oldSaveStyle ? "gam" : "pgn",
\r
4463 title, &number, fileTitle, NULL);
\r
4465 cmailMsgLoaded = FALSE;
\r
4466 if (number == 0) {
\r
4467 int error = GameListBuild(f);
\r
4469 DisplayError(_("Cannot build game list"), error);
\r
4470 } else if (!ListEmpty(&gameList) &&
\r
4471 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4472 GameListPopUp(f, fileTitle);
\r
4475 GameListDestroy();
\r
4478 LoadGame(f, number, fileTitle, FALSE);
\r
4482 int get_term_width()
\r
4487 HFONT hfont, hold_font;
\r
4492 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4496 // get the text metrics
\r
4497 hdc = GetDC(hText);
\r
4498 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4499 if (consoleCF.dwEffects & CFE_BOLD)
\r
4500 lf.lfWeight = FW_BOLD;
\r
4501 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4502 lf.lfItalic = TRUE;
\r
4503 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4504 lf.lfStrikeOut = TRUE;
\r
4505 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4506 lf.lfUnderline = TRUE;
\r
4507 hfont = CreateFontIndirect(&lf);
\r
4508 hold_font = SelectObject(hdc, hfont);
\r
4509 GetTextMetrics(hdc, &tm);
\r
4510 SelectObject(hdc, hold_font);
\r
4511 DeleteObject(hfont);
\r
4512 ReleaseDC(hText, hdc);
\r
4514 // get the rectangle
\r
4515 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4517 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4520 void UpdateICSWidth(HWND hText)
\r
4522 LONG old_width, new_width;
\r
4524 new_width = get_term_width(hText, FALSE);
\r
4525 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4526 if (new_width != old_width)
\r
4528 ics_update_width(new_width);
\r
4529 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4534 ChangedConsoleFont()
\r
4537 CHARRANGE tmpsel, sel;
\r
4538 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4539 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4540 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4543 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4544 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4545 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4546 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4547 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4548 * size. This was undocumented in the version of MSVC++ that I had
\r
4549 * when I wrote the code, but is apparently documented now.
\r
4551 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4552 cfmt.bCharSet = f->lf.lfCharSet;
\r
4553 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4554 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4555 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4556 /* Why are the following seemingly needed too? */
\r
4557 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4558 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4559 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4561 tmpsel.cpMax = -1; /*999999?*/
\r
4562 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4563 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4564 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4565 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4567 paraf.cbSize = sizeof(paraf);
\r
4568 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4569 paraf.dxStartIndent = 0;
\r
4570 paraf.dxOffset = WRAP_INDENT;
\r
4571 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4572 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4573 UpdateICSWidth(hText);
\r
4576 /*---------------------------------------------------------------------------*\
\r
4578 * Window Proc for main window
\r
4580 \*---------------------------------------------------------------------------*/
\r
4582 /* Process messages for main window, etc. */
\r
4584 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4587 int wmId, wmEvent;
\r
4591 char fileTitle[MSG_SIZ];
\r
4592 char buf[MSG_SIZ];
\r
4593 static SnapData sd;
\r
4594 static int peek=0;
\r
4596 switch (message) {
\r
4598 case WM_PAINT: /* message: repaint portion of window */
\r
4602 case WM_ERASEBKGND:
\r
4603 if (IsIconic(hwnd)) {
\r
4604 /* Cheat; change the message */
\r
4605 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4607 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4611 case WM_LBUTTONDOWN:
\r
4612 case WM_MBUTTONDOWN:
\r
4613 case WM_RBUTTONDOWN:
\r
4614 case WM_LBUTTONUP:
\r
4615 case WM_MBUTTONUP:
\r
4616 case WM_RBUTTONUP:
\r
4617 case WM_MOUSEMOVE:
\r
4618 case WM_MOUSEWHEEL:
\r
4619 MouseEvent(hwnd, message, wParam, lParam);
\r
4623 if((char)wParam == '\b') {
\r
4624 ForwardEvent(); peek = 0;
\r
4627 JAWS_KBUP_NAVIGATION
\r
4632 if((char)wParam == '\b') {
\r
4633 if(!peek) BackwardEvent(), peek = 1;
\r
4636 JAWS_KBDOWN_NAVIGATION
\r
4642 JAWS_ALT_INTERCEPT
\r
4644 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4645 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4646 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4647 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4649 SendMessage(h, message, wParam, lParam);
\r
4650 } else if(lParam != KF_REPEAT) {
\r
4651 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4652 TypeInEvent((char)wParam);
\r
4653 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4654 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4659 case WM_PALETTECHANGED:
\r
4660 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4662 HDC hdc = GetDC(hwndMain);
\r
4663 SelectPalette(hdc, hPal, TRUE);
\r
4664 nnew = RealizePalette(hdc);
\r
4666 paletteChanged = TRUE;
\r
4667 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4669 ReleaseDC(hwnd, hdc);
\r
4673 case WM_QUERYNEWPALETTE:
\r
4674 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4676 HDC hdc = GetDC(hwndMain);
\r
4677 paletteChanged = FALSE;
\r
4678 SelectPalette(hdc, hPal, FALSE);
\r
4679 nnew = RealizePalette(hdc);
\r
4681 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4683 ReleaseDC(hwnd, hdc);
\r
4688 case WM_COMMAND: /* message: command from application menu */
\r
4689 wmId = LOWORD(wParam);
\r
4690 wmEvent = HIWORD(wParam);
\r
4695 SAY("new game enter a move to play against the computer with white");
\r
4698 case IDM_NewGameFRC:
\r
4699 if( NewGameFRC() == 0 ) {
\r
4704 case IDM_NewVariant:
\r
4705 NewVariantPopup(hwnd);
\r
4708 case IDM_LoadGame:
\r
4709 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4712 case IDM_LoadNextGame:
\r
4716 case IDM_LoadPrevGame:
\r
4720 case IDM_ReloadGame:
\r
4724 case IDM_LoadPosition:
\r
4725 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4726 Reset(FALSE, TRUE);
\r
4729 f = OpenFileDialog(hwnd, "rb", "",
\r
4730 appData.oldSaveStyle ? "pos" : "fen",
\r
4732 _("Load Position from File"), &number, fileTitle, NULL);
\r
4734 LoadPosition(f, number, fileTitle);
\r
4738 case IDM_LoadNextPosition:
\r
4739 ReloadPosition(1);
\r
4742 case IDM_LoadPrevPosition:
\r
4743 ReloadPosition(-1);
\r
4746 case IDM_ReloadPosition:
\r
4747 ReloadPosition(0);
\r
4750 case IDM_SaveGame:
\r
4751 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4752 f = OpenFileDialog(hwnd, "a", defName,
\r
4753 appData.oldSaveStyle ? "gam" : "pgn",
\r
4755 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4757 SaveGame(f, 0, "");
\r
4761 case IDM_SavePosition:
\r
4762 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4763 f = OpenFileDialog(hwnd, "a", defName,
\r
4764 appData.oldSaveStyle ? "pos" : "fen",
\r
4766 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4768 SavePosition(f, 0, "");
\r
4772 case IDM_SaveDiagram:
\r
4773 defName = "diagram";
\r
4774 f = OpenFileDialog(hwnd, "wb", defName,
\r
4777 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4783 case IDM_CopyGame:
\r
4784 CopyGameToClipboard();
\r
4787 case IDM_PasteGame:
\r
4788 PasteGameFromClipboard();
\r
4791 case IDM_CopyGameListToClipboard:
\r
4792 CopyGameListToClipboard();
\r
4795 /* [AS] Autodetect FEN or PGN data */
\r
4796 case IDM_PasteAny:
\r
4797 PasteGameOrFENFromClipboard();
\r
4800 /* [AS] Move history */
\r
4801 case IDM_ShowMoveHistory:
\r
4802 if( MoveHistoryIsUp() ) {
\r
4803 MoveHistoryPopDown();
\r
4806 MoveHistoryPopUp();
\r
4810 /* [AS] Eval graph */
\r
4811 case IDM_ShowEvalGraph:
\r
4812 if( EvalGraphIsUp() ) {
\r
4813 EvalGraphPopDown();
\r
4817 SetFocus(hwndMain);
\r
4821 /* [AS] Engine output */
\r
4822 case IDM_ShowEngineOutput:
\r
4823 if( EngineOutputIsUp() ) {
\r
4824 EngineOutputPopDown();
\r
4827 EngineOutputPopUp();
\r
4831 /* [AS] User adjudication */
\r
4832 case IDM_UserAdjudication_White:
\r
4833 UserAdjudicationEvent( +1 );
\r
4836 case IDM_UserAdjudication_Black:
\r
4837 UserAdjudicationEvent( -1 );
\r
4840 case IDM_UserAdjudication_Draw:
\r
4841 UserAdjudicationEvent( 0 );
\r
4844 /* [AS] Game list options dialog */
\r
4845 case IDM_GameListOptions:
\r
4846 GameListOptions();
\r
4853 case IDM_CopyPosition:
\r
4854 CopyFENToClipboard();
\r
4857 case IDM_PastePosition:
\r
4858 PasteFENFromClipboard();
\r
4861 case IDM_MailMove:
\r
4865 case IDM_ReloadCMailMsg:
\r
4866 Reset(TRUE, TRUE);
\r
4867 ReloadCmailMsgEvent(FALSE);
\r
4870 case IDM_Minimize:
\r
4871 ShowWindow(hwnd, SW_MINIMIZE);
\r
4878 case IDM_MachineWhite:
\r
4879 MachineWhiteEvent();
\r
4881 * refresh the tags dialog only if it's visible
\r
4883 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4885 tags = PGNTags(&gameInfo);
\r
4886 TagsPopUp(tags, CmailMsg());
\r
4889 SAY("computer starts playing white");
\r
4892 case IDM_MachineBlack:
\r
4893 MachineBlackEvent();
\r
4895 * refresh the tags dialog only if it's visible
\r
4897 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4899 tags = PGNTags(&gameInfo);
\r
4900 TagsPopUp(tags, CmailMsg());
\r
4903 SAY("computer starts playing black");
\r
4906 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4907 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4910 case IDM_TwoMachines:
\r
4911 TwoMachinesEvent();
\r
4913 * refresh the tags dialog only if it's visible
\r
4915 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4917 tags = PGNTags(&gameInfo);
\r
4918 TagsPopUp(tags, CmailMsg());
\r
4921 SAY("computer starts playing both sides");
\r
4924 case IDM_AnalysisMode:
\r
4925 if (!first.analysisSupport) {
\r
4926 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4927 DisplayError(buf, 0);
\r
4929 SAY("analyzing current position");
\r
4930 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4931 if (appData.icsActive) {
\r
4932 if (gameMode != IcsObserving) {
\r
4933 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4934 DisplayError(buf, 0);
\r
4935 /* secure check */
\r
4936 if (appData.icsEngineAnalyze) {
\r
4937 if (appData.debugMode)
\r
4938 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4939 ExitAnalyzeMode();
\r
4945 /* if enable, user want disable icsEngineAnalyze */
\r
4946 if (appData.icsEngineAnalyze) {
\r
4947 ExitAnalyzeMode();
\r
4951 appData.icsEngineAnalyze = TRUE;
\r
4952 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4955 if (!appData.showThinking) ToggleShowThinking();
\r
4956 AnalyzeModeEvent();
\r
4960 case IDM_AnalyzeFile:
\r
4961 if (!first.analysisSupport) {
\r
4962 char buf[MSG_SIZ];
\r
4963 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4964 DisplayError(buf, 0);
\r
4966 if (!appData.showThinking) ToggleShowThinking();
\r
4967 AnalyzeFileEvent();
\r
4968 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4969 AnalysisPeriodicEvent(1);
\r
4973 case IDM_IcsClient:
\r
4977 case IDM_EditGame:
\r
4978 case IDM_EditGame2:
\r
4983 case IDM_EditPosition:
\r
4984 case IDM_EditPosition2:
\r
4985 EditPositionEvent();
\r
4986 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4989 case IDM_Training:
\r
4993 case IDM_ShowGameList:
\r
4994 ShowGameListProc();
\r
4997 case IDM_EditProgs1:
\r
4998 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5001 case IDM_LoadProg1:
\r
5002 LoadEnginePopUp(hwndMain, 0);
\r
5005 case IDM_LoadProg2:
\r
5006 LoadEnginePopUp(hwndMain, 1);
\r
5009 case IDM_EditServers:
\r
5010 EditTagsPopUp(icsNames, &icsNames);
\r
5013 case IDM_EditTags:
\r
5018 case IDM_EditBook:
\r
5022 case IDM_EditComment:
\r
5024 if (commentUp && editComment) {
\r
5027 EditCommentEvent();
\r
5047 case IDM_CallFlag:
\r
5067 case IDM_StopObserving:
\r
5068 StopObservingEvent();
\r
5071 case IDM_StopExamining:
\r
5072 StopExaminingEvent();
\r
5076 UploadGameEvent();
\r
5079 case IDM_TypeInMove:
\r
5080 TypeInEvent('\000');
\r
5083 case IDM_TypeInName:
\r
5084 PopUpNameDialog('\000');
\r
5087 case IDM_Backward:
\r
5089 SetFocus(hwndMain);
\r
5096 SetFocus(hwndMain);
\r
5101 SetFocus(hwndMain);
\r
5106 SetFocus(hwndMain);
\r
5109 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5110 case OPT_GameListPrev:
\r
5111 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5115 RevertEvent(FALSE);
\r
5118 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5119 RevertEvent(TRUE);
\r
5122 case IDM_TruncateGame:
\r
5123 TruncateGameEvent();
\r
5130 case IDM_RetractMove:
\r
5131 RetractMoveEvent();
\r
5134 case IDM_FlipView:
\r
5135 flipView = !flipView;
\r
5136 DrawPosition(FALSE, NULL);
\r
5139 case IDM_FlipClock:
\r
5140 flipClock = !flipClock;
\r
5141 DisplayBothClocks();
\r
5145 case IDM_MuteSounds:
\r
5146 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5147 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5148 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5151 case IDM_GeneralOptions:
\r
5152 GeneralOptionsPopup(hwnd);
\r
5153 DrawPosition(TRUE, NULL);
\r
5156 case IDM_BoardOptions:
\r
5157 BoardOptionsPopup(hwnd);
\r
5160 case IDM_EnginePlayOptions:
\r
5161 EnginePlayOptionsPopup(hwnd);
\r
5164 case IDM_Engine1Options:
\r
5165 EngineOptionsPopup(hwnd, &first);
\r
5168 case IDM_Engine2Options:
\r
5170 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5171 EngineOptionsPopup(hwnd, &second);
\r
5174 case IDM_OptionsUCI:
\r
5175 UciOptionsPopup(hwnd);
\r
5179 TourneyPopup(hwnd);
\r
5182 case IDM_IcsOptions:
\r
5183 IcsOptionsPopup(hwnd);
\r
5187 FontsOptionsPopup(hwnd);
\r
5191 SoundOptionsPopup(hwnd);
\r
5194 case IDM_CommPort:
\r
5195 CommPortOptionsPopup(hwnd);
\r
5198 case IDM_LoadOptions:
\r
5199 LoadOptionsPopup(hwnd);
\r
5202 case IDM_SaveOptions:
\r
5203 SaveOptionsPopup(hwnd);
\r
5206 case IDM_TimeControl:
\r
5207 TimeControlOptionsPopup(hwnd);
\r
5210 case IDM_SaveSettings:
\r
5211 SaveSettings(settingsFileName);
\r
5214 case IDM_SaveSettingsOnExit:
\r
5215 saveSettingsOnExit = !saveSettingsOnExit;
\r
5216 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5217 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5218 MF_CHECKED : MF_UNCHECKED));
\r
5229 case IDM_AboutGame:
\r
5234 appData.debugMode = !appData.debugMode;
\r
5235 if (appData.debugMode) {
\r
5236 char dir[MSG_SIZ];
\r
5237 GetCurrentDirectory(MSG_SIZ, dir);
\r
5238 SetCurrentDirectory(installDir);
\r
5239 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5240 SetCurrentDirectory(dir);
\r
5241 setbuf(debugFP, NULL);
\r
5248 case IDM_HELPCONTENTS:
\r
5249 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5250 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5251 MessageBox (GetFocus(),
\r
5252 _("Unable to activate help"),
\r
5253 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5257 case IDM_HELPSEARCH:
\r
5258 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5259 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5260 MessageBox (GetFocus(),
\r
5261 _("Unable to activate help"),
\r
5262 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5266 case IDM_HELPHELP:
\r
5267 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5268 MessageBox (GetFocus(),
\r
5269 _("Unable to activate help"),
\r
5270 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5275 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5277 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5278 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5279 FreeProcInstance(lpProc);
\r
5282 case IDM_DirectCommand1:
\r
5283 AskQuestionEvent(_("Direct Command"),
\r
5284 _("Send to chess program:"), "", "1");
\r
5286 case IDM_DirectCommand2:
\r
5287 AskQuestionEvent(_("Direct Command"),
\r
5288 _("Send to second chess program:"), "", "2");
\r
5291 case EP_WhitePawn:
\r
5292 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5293 fromX = fromY = -1;
\r
5296 case EP_WhiteKnight:
\r
5297 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5298 fromX = fromY = -1;
\r
5301 case EP_WhiteBishop:
\r
5302 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5303 fromX = fromY = -1;
\r
5306 case EP_WhiteRook:
\r
5307 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5308 fromX = fromY = -1;
\r
5311 case EP_WhiteQueen:
\r
5312 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5313 fromX = fromY = -1;
\r
5316 case EP_WhiteFerz:
\r
5317 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5318 fromX = fromY = -1;
\r
5321 case EP_WhiteWazir:
\r
5322 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5323 fromX = fromY = -1;
\r
5326 case EP_WhiteAlfil:
\r
5327 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5328 fromX = fromY = -1;
\r
5331 case EP_WhiteCannon:
\r
5332 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5333 fromX = fromY = -1;
\r
5336 case EP_WhiteCardinal:
\r
5337 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5338 fromX = fromY = -1;
\r
5341 case EP_WhiteMarshall:
\r
5342 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5343 fromX = fromY = -1;
\r
5346 case EP_WhiteKing:
\r
5347 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5348 fromX = fromY = -1;
\r
5351 case EP_BlackPawn:
\r
5352 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5353 fromX = fromY = -1;
\r
5356 case EP_BlackKnight:
\r
5357 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5358 fromX = fromY = -1;
\r
5361 case EP_BlackBishop:
\r
5362 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5363 fromX = fromY = -1;
\r
5366 case EP_BlackRook:
\r
5367 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5368 fromX = fromY = -1;
\r
5371 case EP_BlackQueen:
\r
5372 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5373 fromX = fromY = -1;
\r
5376 case EP_BlackFerz:
\r
5377 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5378 fromX = fromY = -1;
\r
5381 case EP_BlackWazir:
\r
5382 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5383 fromX = fromY = -1;
\r
5386 case EP_BlackAlfil:
\r
5387 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5388 fromX = fromY = -1;
\r
5391 case EP_BlackCannon:
\r
5392 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5393 fromX = fromY = -1;
\r
5396 case EP_BlackCardinal:
\r
5397 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5398 fromX = fromY = -1;
\r
5401 case EP_BlackMarshall:
\r
5402 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5403 fromX = fromY = -1;
\r
5406 case EP_BlackKing:
\r
5407 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5408 fromX = fromY = -1;
\r
5411 case EP_EmptySquare:
\r
5412 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5413 fromX = fromY = -1;
\r
5416 case EP_ClearBoard:
\r
5417 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5418 fromX = fromY = -1;
\r
5422 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5423 fromX = fromY = -1;
\r
5427 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5428 fromX = fromY = -1;
\r
5432 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5433 fromX = fromY = -1;
\r
5437 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5438 fromX = fromY = -1;
\r
5442 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5443 fromX = fromY = -1;
\r
5447 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5448 fromX = fromY = -1;
\r
5452 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5453 fromX = fromY = -1;
\r
5457 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5458 fromX = fromY = -1;
\r
5462 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5463 fromX = fromY = -1;
\r
5467 barbaric = 0; appData.language = "";
\r
5468 TranslateMenus(0);
\r
5469 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5470 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5471 lastChecked = wmId;
\r
5475 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5476 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5478 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5479 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5480 TranslateMenus(0);
\r
5481 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5482 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5483 lastChecked = wmId;
\r
5486 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5492 case CLOCK_TIMER_ID:
\r
5493 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5494 clockTimerEvent = 0;
\r
5495 DecrementClocks(); /* call into back end */
\r
5497 case LOAD_GAME_TIMER_ID:
\r
5498 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5499 loadGameTimerEvent = 0;
\r
5500 AutoPlayGameLoop(); /* call into back end */
\r
5502 case ANALYSIS_TIMER_ID:
\r
5503 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5504 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5505 AnalysisPeriodicEvent(0);
\r
5507 KillTimer(hwnd, analysisTimerEvent);
\r
5508 analysisTimerEvent = 0;
\r
5511 case DELAYED_TIMER_ID:
\r
5512 KillTimer(hwnd, delayedTimerEvent);
\r
5513 delayedTimerEvent = 0;
\r
5514 delayedTimerCallback();
\r
5519 case WM_USER_Input:
\r
5520 InputEvent(hwnd, message, wParam, lParam);
\r
5523 /* [AS] Also move "attached" child windows */
\r
5524 case WM_WINDOWPOSCHANGING:
\r
5526 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5527 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5529 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5530 /* Window is moving */
\r
5533 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5534 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5535 rcMain.right = wpMain.x + wpMain.width;
\r
5536 rcMain.top = wpMain.y;
\r
5537 rcMain.bottom = wpMain.y + wpMain.height;
\r
5539 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5540 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5541 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5542 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5543 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5544 wpMain.x = lpwp->x;
\r
5545 wpMain.y = lpwp->y;
\r
5550 /* [AS] Snapping */
\r
5551 case WM_ENTERSIZEMOVE:
\r
5552 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5553 if (hwnd == hwndMain) {
\r
5554 doingSizing = TRUE;
\r
5557 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5561 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5562 if (hwnd == hwndMain) {
\r
5563 lastSizing = wParam;
\r
5568 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5569 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5571 case WM_EXITSIZEMOVE:
\r
5572 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5573 if (hwnd == hwndMain) {
\r
5575 doingSizing = FALSE;
\r
5576 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5577 GetClientRect(hwnd, &client);
\r
5578 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5580 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5582 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5585 case WM_DESTROY: /* message: window being destroyed */
\r
5586 PostQuitMessage(0);
\r
5590 if (hwnd == hwndMain) {
\r
5595 default: /* Passes it on if unprocessed */
\r
5596 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5601 /*---------------------------------------------------------------------------*\
\r
5603 * Misc utility routines
\r
5605 \*---------------------------------------------------------------------------*/
\r
5608 * Decent random number generator, at least not as bad as Windows
\r
5609 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5611 unsigned int randstate;
\r
5616 randstate = randstate * 1664525 + 1013904223;
\r
5617 return (int) randstate & 0x7fffffff;
\r
5621 mysrandom(unsigned int seed)
\r
5628 * returns TRUE if user selects a different color, FALSE otherwise
\r
5632 ChangeColor(HWND hwnd, COLORREF *which)
\r
5634 static BOOL firstTime = TRUE;
\r
5635 static DWORD customColors[16];
\r
5637 COLORREF newcolor;
\r
5642 /* Make initial colors in use available as custom colors */
\r
5643 /* Should we put the compiled-in defaults here instead? */
\r
5645 customColors[i++] = lightSquareColor & 0xffffff;
\r
5646 customColors[i++] = darkSquareColor & 0xffffff;
\r
5647 customColors[i++] = whitePieceColor & 0xffffff;
\r
5648 customColors[i++] = blackPieceColor & 0xffffff;
\r
5649 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5650 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5652 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5653 customColors[i++] = textAttribs[ccl].color;
\r
5655 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5656 firstTime = FALSE;
\r
5659 cc.lStructSize = sizeof(cc);
\r
5660 cc.hwndOwner = hwnd;
\r
5661 cc.hInstance = NULL;
\r
5662 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5663 cc.lpCustColors = (LPDWORD) customColors;
\r
5664 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5666 if (!ChooseColor(&cc)) return FALSE;
\r
5668 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5669 if (newcolor == *which) return FALSE;
\r
5670 *which = newcolor;
\r
5674 InitDrawingColors();
\r
5675 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5680 MyLoadSound(MySound *ms)
\r
5686 if (ms->data && ms->flag) free(ms->data);
\r
5689 switch (ms->name[0]) {
\r
5695 /* System sound from Control Panel. Don't preload here. */
\r
5699 if (ms->name[1] == NULLCHAR) {
\r
5700 /* "!" alone = silence */
\r
5703 /* Builtin wave resource. Error if not found. */
\r
5704 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5705 if (h == NULL) break;
\r
5706 ms->data = (void *)LoadResource(hInst, h);
\r
5707 ms->flag = 0; // not maloced, so cannot be freed!
\r
5708 if (h == NULL) break;
\r
5713 /* .wav file. Error if not found. */
\r
5714 f = fopen(ms->name, "rb");
\r
5715 if (f == NULL) break;
\r
5716 if (fstat(fileno(f), &st) < 0) break;
\r
5717 ms->data = malloc(st.st_size);
\r
5719 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5725 char buf[MSG_SIZ];
\r
5726 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5727 DisplayError(buf, GetLastError());
\r
5733 MyPlaySound(MySound *ms)
\r
5735 BOOLEAN ok = FALSE;
\r
5737 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5738 switch (ms->name[0]) {
\r
5740 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5745 /* System sound from Control Panel (deprecated feature).
\r
5746 "$" alone or an unset sound name gets default beep (still in use). */
\r
5747 if (ms->name[1]) {
\r
5748 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5750 if (!ok) ok = MessageBeep(MB_OK);
\r
5753 /* Builtin wave resource, or "!" alone for silence */
\r
5754 if (ms->name[1]) {
\r
5755 if (ms->data == NULL) return FALSE;
\r
5756 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5762 /* .wav file. Error if not found. */
\r
5763 if (ms->data == NULL) return FALSE;
\r
5764 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5767 /* Don't print an error: this can happen innocently if the sound driver
\r
5768 is busy; for instance, if another instance of WinBoard is playing
\r
5769 a sound at about the same time. */
\r
5775 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5778 OPENFILENAME *ofn;
\r
5779 static UINT *number; /* gross that this is static */
\r
5781 switch (message) {
\r
5782 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5783 /* Center the dialog over the application window */
\r
5784 ofn = (OPENFILENAME *) lParam;
\r
5785 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5786 number = (UINT *) ofn->lCustData;
\r
5787 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5791 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5792 Translate(hDlg, 1536);
\r
5793 return FALSE; /* Allow for further processing */
\r
5796 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5797 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5799 return FALSE; /* Allow for further processing */
\r
5805 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5807 static UINT *number;
\r
5808 OPENFILENAME *ofname;
\r
5811 case WM_INITDIALOG:
\r
5812 Translate(hdlg, DLG_IndexNumber);
\r
5813 ofname = (OPENFILENAME *)lParam;
\r
5814 number = (UINT *)(ofname->lCustData);
\r
5817 ofnot = (OFNOTIFY *)lParam;
\r
5818 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5819 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5828 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5829 char *nameFilt, char *dlgTitle, UINT *number,
\r
5830 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5832 OPENFILENAME openFileName;
\r
5833 char buf1[MSG_SIZ];
\r
5836 if (fileName == NULL) fileName = buf1;
\r
5837 if (defName == NULL) {
\r
5838 safeStrCpy(fileName, "*.", 3 );
\r
5839 strcat(fileName, defExt);
\r
5841 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5843 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5844 if (number) *number = 0;
\r
5846 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5847 openFileName.hwndOwner = hwnd;
\r
5848 openFileName.hInstance = (HANDLE) hInst;
\r
5849 openFileName.lpstrFilter = nameFilt;
\r
5850 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5851 openFileName.nMaxCustFilter = 0L;
\r
5852 openFileName.nFilterIndex = 1L;
\r
5853 openFileName.lpstrFile = fileName;
\r
5854 openFileName.nMaxFile = MSG_SIZ;
\r
5855 openFileName.lpstrFileTitle = fileTitle;
\r
5856 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5857 openFileName.lpstrInitialDir = NULL;
\r
5858 openFileName.lpstrTitle = dlgTitle;
\r
5859 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5860 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5861 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5862 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5863 openFileName.nFileOffset = 0;
\r
5864 openFileName.nFileExtension = 0;
\r
5865 openFileName.lpstrDefExt = defExt;
\r
5866 openFileName.lCustData = (LONG) number;
\r
5867 openFileName.lpfnHook = oldDialog ?
\r
5868 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5869 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5871 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5872 GetOpenFileName(&openFileName)) {
\r
5873 /* open the file */
\r
5874 f = fopen(openFileName.lpstrFile, write);
\r
5876 MessageBox(hwnd, _("File open failed"), NULL,
\r
5877 MB_OK|MB_ICONEXCLAMATION);
\r
5881 int err = CommDlgExtendedError();
\r
5882 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5891 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5893 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5896 * Get the first pop-up menu in the menu template. This is the
\r
5897 * menu that TrackPopupMenu displays.
\r
5899 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5900 TranslateOneMenu(10, hmenuTrackPopup);
\r
5902 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5905 * TrackPopup uses screen coordinates, so convert the
\r
5906 * coordinates of the mouse click to screen coordinates.
\r
5908 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5910 /* Draw and track the floating pop-up menu. */
\r
5911 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5912 pt.x, pt.y, 0, hwnd, NULL);
\r
5914 /* Destroy the menu.*/
\r
5915 DestroyMenu(hmenu);
\r
5920 int sizeX, sizeY, newSizeX, newSizeY;
\r
5922 } ResizeEditPlusButtonsClosure;
\r
5925 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5927 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5931 if (hChild == cl->hText) return TRUE;
\r
5932 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5933 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5934 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5935 ScreenToClient(cl->hDlg, &pt);
\r
5936 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5937 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5941 /* Resize a dialog that has a (rich) edit field filling most of
\r
5942 the top, with a row of buttons below */
\r
5944 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5947 int newTextHeight, newTextWidth;
\r
5948 ResizeEditPlusButtonsClosure cl;
\r
5950 /*if (IsIconic(hDlg)) return;*/
\r
5951 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5953 cl.hdwp = BeginDeferWindowPos(8);
\r
5955 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5956 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5957 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5958 if (newTextHeight < 0) {
\r
5959 newSizeY += -newTextHeight;
\r
5960 newTextHeight = 0;
\r
5962 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5963 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5969 cl.newSizeX = newSizeX;
\r
5970 cl.newSizeY = newSizeY;
\r
5971 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5973 EndDeferWindowPos(cl.hdwp);
\r
5976 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5978 RECT rChild, rParent;
\r
5979 int wChild, hChild, wParent, hParent;
\r
5980 int wScreen, hScreen, xNew, yNew;
\r
5983 /* Get the Height and Width of the child window */
\r
5984 GetWindowRect (hwndChild, &rChild);
\r
5985 wChild = rChild.right - rChild.left;
\r
5986 hChild = rChild.bottom - rChild.top;
\r
5988 /* Get the Height and Width of the parent window */
\r
5989 GetWindowRect (hwndParent, &rParent);
\r
5990 wParent = rParent.right - rParent.left;
\r
5991 hParent = rParent.bottom - rParent.top;
\r
5993 /* Get the display limits */
\r
5994 hdc = GetDC (hwndChild);
\r
5995 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5996 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5997 ReleaseDC(hwndChild, hdc);
\r
5999 /* Calculate new X position, then adjust for screen */
\r
6000 xNew = rParent.left + ((wParent - wChild) /2);
\r
6003 } else if ((xNew+wChild) > wScreen) {
\r
6004 xNew = wScreen - wChild;
\r
6007 /* Calculate new Y position, then adjust for screen */
\r
6009 yNew = rParent.top + ((hParent - hChild) /2);
\r
6012 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6017 } else if ((yNew+hChild) > hScreen) {
\r
6018 yNew = hScreen - hChild;
\r
6021 /* Set it, and return */
\r
6022 return SetWindowPos (hwndChild, NULL,
\r
6023 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6026 /* Center one window over another */
\r
6027 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6029 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6032 /*---------------------------------------------------------------------------*\
\r
6034 * Startup Dialog functions
\r
6036 \*---------------------------------------------------------------------------*/
\r
6038 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6040 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6042 while (*cd != NULL) {
\r
6043 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6049 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6051 char buf1[MAX_ARG_LEN];
\r
6054 if (str[0] == '@') {
\r
6055 FILE* f = fopen(str + 1, "r");
\r
6057 DisplayFatalError(str + 1, errno, 2);
\r
6060 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6062 buf1[len] = NULLCHAR;
\r
6066 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6069 char buf[MSG_SIZ];
\r
6070 char *end = strchr(str, '\n');
\r
6071 if (end == NULL) return;
\r
6072 memcpy(buf, str, end - str);
\r
6073 buf[end - str] = NULLCHAR;
\r
6074 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6080 SetStartupDialogEnables(HWND hDlg)
\r
6082 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6083 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6084 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6085 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6086 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6087 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6088 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6089 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6090 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6091 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6092 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6093 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6094 IsDlgButtonChecked(hDlg, OPT_View));
\r
6098 QuoteForFilename(char *filename)
\r
6100 int dquote, space;
\r
6101 dquote = strchr(filename, '"') != NULL;
\r
6102 space = strchr(filename, ' ') != NULL;
\r
6103 if (dquote || space) {
\r
6115 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6117 char buf[MSG_SIZ];
\r
6120 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6121 q = QuoteForFilename(nthcp);
\r
6122 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6123 if (*nthdir != NULLCHAR) {
\r
6124 q = QuoteForFilename(nthdir);
\r
6125 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6127 if (*nthcp == NULLCHAR) {
\r
6128 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6129 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6130 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6131 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6136 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6138 char buf[MSG_SIZ];
\r
6142 switch (message) {
\r
6143 case WM_INITDIALOG:
\r
6144 /* Center the dialog */
\r
6145 CenterWindow (hDlg, GetDesktopWindow());
\r
6146 Translate(hDlg, DLG_Startup);
\r
6147 /* Initialize the dialog items */
\r
6148 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6149 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6150 firstChessProgramNames);
\r
6151 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6152 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6153 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6154 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6155 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6156 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6157 if (*appData.icsHelper != NULLCHAR) {
\r
6158 char *q = QuoteForFilename(appData.icsHelper);
\r
6159 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6161 if (*appData.icsHost == NULLCHAR) {
\r
6162 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6163 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6164 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6165 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6166 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6169 if (appData.icsActive) {
\r
6170 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6172 else if (appData.noChessProgram) {
\r
6173 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6176 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6179 SetStartupDialogEnables(hDlg);
\r
6183 switch (LOWORD(wParam)) {
\r
6185 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6186 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6187 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6189 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6190 ParseArgs(StringGet, &p);
\r
6191 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6192 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6194 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6195 ParseArgs(StringGet, &p);
\r
6196 SwapEngines(singleList); // ... and then make it 'second'
\r
6197 appData.noChessProgram = FALSE;
\r
6198 appData.icsActive = FALSE;
\r
6199 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6200 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6201 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6203 ParseArgs(StringGet, &p);
\r
6204 if (appData.zippyPlay) {
\r
6205 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6206 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6208 ParseArgs(StringGet, &p);
\r
6210 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6211 appData.noChessProgram = TRUE;
\r
6212 appData.icsActive = FALSE;
\r
6214 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6215 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6218 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6219 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6221 ParseArgs(StringGet, &p);
\r
6223 EndDialog(hDlg, TRUE);
\r
6230 case IDM_HELPCONTENTS:
\r
6231 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6232 MessageBox (GetFocus(),
\r
6233 _("Unable to activate help"),
\r
6234 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6239 SetStartupDialogEnables(hDlg);
\r
6247 /*---------------------------------------------------------------------------*\
\r
6249 * About box dialog functions
\r
6251 \*---------------------------------------------------------------------------*/
\r
6253 /* Process messages for "About" dialog box */
\r
6255 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6257 switch (message) {
\r
6258 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6259 /* Center the dialog over the application window */
\r
6260 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6261 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6262 Translate(hDlg, ABOUTBOX);
\r
6266 case WM_COMMAND: /* message: received a command */
\r
6267 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6268 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6269 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6277 /*---------------------------------------------------------------------------*\
\r
6279 * Comment Dialog functions
\r
6281 \*---------------------------------------------------------------------------*/
\r
6284 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6286 static HANDLE hwndText = NULL;
\r
6287 int len, newSizeX, newSizeY, flags;
\r
6288 static int sizeX, sizeY;
\r
6293 switch (message) {
\r
6294 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6295 /* Initialize the dialog items */
\r
6296 Translate(hDlg, DLG_EditComment);
\r
6297 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6298 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6299 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6300 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6301 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6302 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6303 SetWindowText(hDlg, commentTitle);
\r
6304 if (editComment) {
\r
6305 SetFocus(hwndText);
\r
6307 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6309 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6310 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6311 MAKELPARAM(FALSE, 0));
\r
6312 /* Size and position the dialog */
\r
6313 if (!commentDialog) {
\r
6314 commentDialog = hDlg;
\r
6315 flags = SWP_NOZORDER;
\r
6316 GetClientRect(hDlg, &rect);
\r
6317 sizeX = rect.right;
\r
6318 sizeY = rect.bottom;
\r
6319 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6320 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6321 WINDOWPLACEMENT wp;
\r
6322 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6323 wp.length = sizeof(WINDOWPLACEMENT);
\r
6325 wp.showCmd = SW_SHOW;
\r
6326 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6327 wp.rcNormalPosition.left = wpComment.x;
\r
6328 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6329 wp.rcNormalPosition.top = wpComment.y;
\r
6330 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6331 SetWindowPlacement(hDlg, &wp);
\r
6333 GetClientRect(hDlg, &rect);
\r
6334 newSizeX = rect.right;
\r
6335 newSizeY = rect.bottom;
\r
6336 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6337 newSizeX, newSizeY);
\r
6342 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6345 case WM_COMMAND: /* message: received a command */
\r
6346 switch (LOWORD(wParam)) {
\r
6348 if (editComment) {
\r
6350 /* Read changed options from the dialog box */
\r
6351 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6352 len = GetWindowTextLength(hwndText);
\r
6353 str = (char *) malloc(len + 1);
\r
6354 GetWindowText(hwndText, str, len + 1);
\r
6363 ReplaceComment(commentIndex, str);
\r
6370 case OPT_CancelComment:
\r
6374 case OPT_ClearComment:
\r
6375 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6378 case OPT_EditComment:
\r
6379 EditCommentEvent();
\r
6387 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6388 if( wParam == OPT_CommentText ) {
\r
6389 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6391 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6392 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6396 pt.x = LOWORD( lpMF->lParam );
\r
6397 pt.y = HIWORD( lpMF->lParam );
\r
6399 if(lpMF->msg == WM_CHAR) {
\r
6401 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6402 index = sel.cpMin;
\r
6404 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6406 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6407 len = GetWindowTextLength(hwndText);
\r
6408 str = (char *) malloc(len + 1);
\r
6409 GetWindowText(hwndText, str, len + 1);
\r
6410 ReplaceComment(commentIndex, str);
\r
6411 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6412 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6415 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6416 lpMF->msg = WM_USER;
\r
6424 newSizeX = LOWORD(lParam);
\r
6425 newSizeY = HIWORD(lParam);
\r
6426 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6431 case WM_GETMINMAXINFO:
\r
6432 /* Prevent resizing window too small */
\r
6433 mmi = (MINMAXINFO *) lParam;
\r
6434 mmi->ptMinTrackSize.x = 100;
\r
6435 mmi->ptMinTrackSize.y = 100;
\r
6442 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6447 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6449 if (str == NULL) str = "";
\r
6450 p = (char *) malloc(2 * strlen(str) + 2);
\r
6453 if (*str == '\n') *q++ = '\r';
\r
6457 if (commentText != NULL) free(commentText);
\r
6459 commentIndex = index;
\r
6460 commentTitle = title;
\r
6462 editComment = edit;
\r
6464 if (commentDialog) {
\r
6465 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6466 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6468 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6469 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6470 hwndMain, (DLGPROC)lpProc);
\r
6471 FreeProcInstance(lpProc);
\r
6477 /*---------------------------------------------------------------------------*\
\r
6479 * Type-in move dialog functions
\r
6481 \*---------------------------------------------------------------------------*/
\r
6484 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6486 char move[MSG_SIZ];
\r
6489 switch (message) {
\r
6490 case WM_INITDIALOG:
\r
6491 move[0] = (char) lParam;
\r
6492 move[1] = NULLCHAR;
\r
6493 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6494 Translate(hDlg, DLG_TypeInMove);
\r
6495 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6496 SetWindowText(hInput, move);
\r
6498 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6502 switch (LOWORD(wParam)) {
\r
6505 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6506 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6507 TypeInDoneEvent(move);
\r
6508 EndDialog(hDlg, TRUE);
\r
6511 EndDialog(hDlg, FALSE);
\r
6522 PopUpMoveDialog(char firstchar)
\r
6526 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6527 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6528 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6529 FreeProcInstance(lpProc);
\r
6532 /*---------------------------------------------------------------------------*\
\r
6534 * Type-in name dialog functions
\r
6536 \*---------------------------------------------------------------------------*/
\r
6539 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6541 char move[MSG_SIZ];
\r
6544 switch (message) {
\r
6545 case WM_INITDIALOG:
\r
6546 move[0] = (char) lParam;
\r
6547 move[1] = NULLCHAR;
\r
6548 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6549 Translate(hDlg, DLG_TypeInName);
\r
6550 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6551 SetWindowText(hInput, move);
\r
6553 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6557 switch (LOWORD(wParam)) {
\r
6559 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6560 appData.userName = strdup(move);
\r
6563 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6564 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6565 DisplayTitle(move);
\r
6569 EndDialog(hDlg, TRUE);
\r
6572 EndDialog(hDlg, FALSE);
\r
6583 PopUpNameDialog(char firstchar)
\r
6587 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6588 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6589 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6590 FreeProcInstance(lpProc);
\r
6593 /*---------------------------------------------------------------------------*\
\r
6597 \*---------------------------------------------------------------------------*/
\r
6599 /* Nonmodal error box */
\r
6600 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6601 WPARAM wParam, LPARAM lParam);
\r
6604 ErrorPopUp(char *title, char *content)
\r
6608 BOOLEAN modal = hwndMain == NULL;
\r
6626 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6627 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6630 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6632 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6633 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6634 hwndMain, (DLGPROC)lpProc);
\r
6635 FreeProcInstance(lpProc);
\r
6642 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6643 if (errorDialog == NULL) return;
\r
6644 DestroyWindow(errorDialog);
\r
6645 errorDialog = NULL;
\r
6646 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6650 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6655 switch (message) {
\r
6656 case WM_INITDIALOG:
\r
6657 GetWindowRect(hDlg, &rChild);
\r
6660 SetWindowPos(hDlg, NULL, rChild.left,
\r
6661 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6662 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6666 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6667 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6668 and it doesn't work when you resize the dialog.
\r
6669 For now, just give it a default position.
\r
6671 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6672 Translate(hDlg, DLG_Error);
\r
6674 errorDialog = hDlg;
\r
6675 SetWindowText(hDlg, errorTitle);
\r
6676 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6677 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6681 switch (LOWORD(wParam)) {
\r
6684 if (errorDialog == hDlg) errorDialog = NULL;
\r
6685 DestroyWindow(hDlg);
\r
6697 HWND gothicDialog = NULL;
\r
6700 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6704 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6706 switch (message) {
\r
6707 case WM_INITDIALOG:
\r
6708 GetWindowRect(hDlg, &rChild);
\r
6710 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6714 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6715 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6716 and it doesn't work when you resize the dialog.
\r
6717 For now, just give it a default position.
\r
6719 gothicDialog = hDlg;
\r
6720 SetWindowText(hDlg, errorTitle);
\r
6721 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6722 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6726 switch (LOWORD(wParam)) {
\r
6729 if (errorDialog == hDlg) errorDialog = NULL;
\r
6730 DestroyWindow(hDlg);
\r
6742 GothicPopUp(char *title, VariantClass variant)
\r
6745 static char *lastTitle;
\r
6747 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6748 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6750 if(lastTitle != title && gothicDialog != NULL) {
\r
6751 DestroyWindow(gothicDialog);
\r
6752 gothicDialog = NULL;
\r
6754 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6755 title = lastTitle;
\r
6756 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6757 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6758 hwndMain, (DLGPROC)lpProc);
\r
6759 FreeProcInstance(lpProc);
\r
6764 /*---------------------------------------------------------------------------*\
\r
6766 * Ics Interaction console functions
\r
6768 \*---------------------------------------------------------------------------*/
\r
6770 #define HISTORY_SIZE 64
\r
6771 static char *history[HISTORY_SIZE];
\r
6772 int histIn = 0, histP = 0;
\r
6775 SaveInHistory(char *cmd)
\r
6777 if (history[histIn] != NULL) {
\r
6778 free(history[histIn]);
\r
6779 history[histIn] = NULL;
\r
6781 if (*cmd == NULLCHAR) return;
\r
6782 history[histIn] = StrSave(cmd);
\r
6783 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6784 if (history[histIn] != NULL) {
\r
6785 free(history[histIn]);
\r
6786 history[histIn] = NULL;
\r
6792 PrevInHistory(char *cmd)
\r
6795 if (histP == histIn) {
\r
6796 if (history[histIn] != NULL) free(history[histIn]);
\r
6797 history[histIn] = StrSave(cmd);
\r
6799 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6800 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6802 return history[histP];
\r
6808 if (histP == histIn) return NULL;
\r
6809 histP = (histP + 1) % HISTORY_SIZE;
\r
6810 return history[histP];
\r
6814 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6818 hmenu = LoadMenu(hInst, "TextMenu");
\r
6819 h = GetSubMenu(hmenu, 0);
\r
6821 if (strcmp(e->item, "-") == 0) {
\r
6822 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6823 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6824 int flags = MF_STRING, j = 0;
\r
6825 if (e->item[0] == '|') {
\r
6826 flags |= MF_MENUBARBREAK;
\r
6829 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6830 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6838 WNDPROC consoleTextWindowProc;
\r
6841 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6843 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6844 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6848 SetWindowText(hInput, command);
\r
6850 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6852 sel.cpMin = 999999;
\r
6853 sel.cpMax = 999999;
\r
6854 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6859 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6860 if (sel.cpMin == sel.cpMax) {
\r
6861 /* Expand to surrounding word */
\r
6864 tr.chrg.cpMax = sel.cpMin;
\r
6865 tr.chrg.cpMin = --sel.cpMin;
\r
6866 if (sel.cpMin < 0) break;
\r
6867 tr.lpstrText = name;
\r
6868 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6869 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6873 tr.chrg.cpMin = sel.cpMax;
\r
6874 tr.chrg.cpMax = ++sel.cpMax;
\r
6875 tr.lpstrText = name;
\r
6876 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6877 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6880 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6881 MessageBeep(MB_ICONEXCLAMATION);
\r
6885 tr.lpstrText = name;
\r
6886 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6888 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6889 MessageBeep(MB_ICONEXCLAMATION);
\r
6892 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6895 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6896 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6897 SetWindowText(hInput, buf);
\r
6898 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6900 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6901 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6902 SetWindowText(hInput, buf);
\r
6903 sel.cpMin = 999999;
\r
6904 sel.cpMax = 999999;
\r
6905 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6911 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6916 switch (message) {
\r
6918 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6919 if(wParam=='R') return 0;
\r
6922 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6925 sel.cpMin = 999999;
\r
6926 sel.cpMax = 999999;
\r
6927 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6928 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6933 if(wParam != '\022') {
\r
6934 if (wParam == '\t') {
\r
6935 if (GetKeyState(VK_SHIFT) < 0) {
\r
6937 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6938 if (buttonDesc[0].hwnd) {
\r
6939 SetFocus(buttonDesc[0].hwnd);
\r
6941 SetFocus(hwndMain);
\r
6945 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6948 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6949 JAWS_DELETE( SetFocus(hInput); )
\r
6950 SendMessage(hInput, message, wParam, lParam);
\r
6953 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6955 case WM_RBUTTONDOWN:
\r
6956 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6957 /* Move selection here if it was empty */
\r
6959 pt.x = LOWORD(lParam);
\r
6960 pt.y = HIWORD(lParam);
\r
6961 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6962 if (sel.cpMin == sel.cpMax) {
\r
6963 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6964 sel.cpMax = sel.cpMin;
\r
6965 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6967 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6968 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6970 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6971 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6972 if (sel.cpMin == sel.cpMax) {
\r
6973 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6974 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6976 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6977 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6979 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6980 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6981 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6982 MenuPopup(hwnd, pt, hmenu, -1);
\r
6986 case WM_RBUTTONUP:
\r
6987 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6988 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6989 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6993 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6995 return SendMessage(hInput, message, wParam, lParam);
\r
6996 case WM_MBUTTONDOWN:
\r
6997 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6999 switch (LOWORD(wParam)) {
\r
7000 case IDM_QuickPaste:
\r
7002 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7003 if (sel.cpMin == sel.cpMax) {
\r
7004 MessageBeep(MB_ICONEXCLAMATION);
\r
7007 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7008 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7009 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7014 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7017 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7020 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7024 int i = LOWORD(wParam) - IDM_CommandX;
\r
7025 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7026 icsTextMenuEntry[i].command != NULL) {
\r
7027 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7028 icsTextMenuEntry[i].getname,
\r
7029 icsTextMenuEntry[i].immediate);
\r
7037 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7040 WNDPROC consoleInputWindowProc;
\r
7043 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7045 char buf[MSG_SIZ];
\r
7047 static BOOL sendNextChar = FALSE;
\r
7048 static BOOL quoteNextChar = FALSE;
\r
7049 InputSource *is = consoleInputSource;
\r
7053 switch (message) {
\r
7055 if (!appData.localLineEditing || sendNextChar) {
\r
7056 is->buf[0] = (CHAR) wParam;
\r
7058 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7059 sendNextChar = FALSE;
\r
7062 if (quoteNextChar) {
\r
7063 buf[0] = (char) wParam;
\r
7064 buf[1] = NULLCHAR;
\r
7065 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7066 quoteNextChar = FALSE;
\r
7070 case '\r': /* Enter key */
\r
7071 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7072 if (consoleEcho) SaveInHistory(is->buf);
\r
7073 is->buf[is->count++] = '\n';
\r
7074 is->buf[is->count] = NULLCHAR;
\r
7075 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7076 if (consoleEcho) {
\r
7077 ConsoleOutput(is->buf, is->count, TRUE);
\r
7078 } else if (appData.localLineEditing) {
\r
7079 ConsoleOutput("\n", 1, TRUE);
\r
7082 case '\033': /* Escape key */
\r
7083 SetWindowText(hwnd, "");
\r
7084 cf.cbSize = sizeof(CHARFORMAT);
\r
7085 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7086 if (consoleEcho) {
\r
7087 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7089 cf.crTextColor = COLOR_ECHOOFF;
\r
7091 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7092 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7094 case '\t': /* Tab key */
\r
7095 if (GetKeyState(VK_SHIFT) < 0) {
\r
7097 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7100 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7101 if (buttonDesc[0].hwnd) {
\r
7102 SetFocus(buttonDesc[0].hwnd);
\r
7104 SetFocus(hwndMain);
\r
7108 case '\023': /* Ctrl+S */
\r
7109 sendNextChar = TRUE;
\r
7111 case '\021': /* Ctrl+Q */
\r
7112 quoteNextChar = TRUE;
\r
7122 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7123 p = PrevInHistory(buf);
\r
7125 SetWindowText(hwnd, p);
\r
7126 sel.cpMin = 999999;
\r
7127 sel.cpMax = 999999;
\r
7128 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7133 p = NextInHistory();
\r
7135 SetWindowText(hwnd, p);
\r
7136 sel.cpMin = 999999;
\r
7137 sel.cpMax = 999999;
\r
7138 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7144 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7148 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7152 case WM_MBUTTONDOWN:
\r
7153 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7154 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7156 case WM_RBUTTONUP:
\r
7157 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7158 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7159 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7163 hmenu = LoadMenu(hInst, "InputMenu");
\r
7164 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7165 if (sel.cpMin == sel.cpMax) {
\r
7166 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7167 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7169 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7170 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7172 pt.x = LOWORD(lParam);
\r
7173 pt.y = HIWORD(lParam);
\r
7174 MenuPopup(hwnd, pt, hmenu, -1);
\r
7178 switch (LOWORD(wParam)) {
\r
7180 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7182 case IDM_SelectAll:
\r
7184 sel.cpMax = -1; /*999999?*/
\r
7185 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7188 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7191 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7194 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7199 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7202 #define CO_MAX 100000
\r
7203 #define CO_TRIM 1000
\r
7206 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7208 static SnapData sd;
\r
7209 HWND hText, hInput;
\r
7211 static int sizeX, sizeY;
\r
7212 int newSizeX, newSizeY;
\r
7216 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7217 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7219 switch (message) {
\r
7221 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7223 ENLINK *pLink = (ENLINK*)lParam;
\r
7224 if (pLink->msg == WM_LBUTTONUP)
\r
7228 tr.chrg = pLink->chrg;
\r
7229 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7230 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7231 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7232 free(tr.lpstrText);
\r
7236 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7237 hwndConsole = hDlg;
\r
7239 consoleTextWindowProc = (WNDPROC)
\r
7240 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7241 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7242 consoleInputWindowProc = (WNDPROC)
\r
7243 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7244 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7245 Colorize(ColorNormal, TRUE);
\r
7246 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7247 ChangedConsoleFont();
\r
7248 GetClientRect(hDlg, &rect);
\r
7249 sizeX = rect.right;
\r
7250 sizeY = rect.bottom;
\r
7251 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7252 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7253 WINDOWPLACEMENT wp;
\r
7254 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7255 wp.length = sizeof(WINDOWPLACEMENT);
\r
7257 wp.showCmd = SW_SHOW;
\r
7258 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7259 wp.rcNormalPosition.left = wpConsole.x;
\r
7260 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7261 wp.rcNormalPosition.top = wpConsole.y;
\r
7262 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7263 SetWindowPlacement(hDlg, &wp);
\r
7266 // [HGM] Chessknight's change 2004-07-13
\r
7267 else { /* Determine Defaults */
\r
7268 WINDOWPLACEMENT wp;
\r
7269 wpConsole.x = wpMain.width + 1;
\r
7270 wpConsole.y = wpMain.y;
\r
7271 wpConsole.width = screenWidth - wpMain.width;
\r
7272 wpConsole.height = wpMain.height;
\r
7273 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7274 wp.length = sizeof(WINDOWPLACEMENT);
\r
7276 wp.showCmd = SW_SHOW;
\r
7277 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7278 wp.rcNormalPosition.left = wpConsole.x;
\r
7279 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7280 wp.rcNormalPosition.top = wpConsole.y;
\r
7281 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7282 SetWindowPlacement(hDlg, &wp);
\r
7285 // Allow hText to highlight URLs and send notifications on them
\r
7286 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7287 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7288 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7289 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7303 if (IsIconic(hDlg)) break;
\r
7304 newSizeX = LOWORD(lParam);
\r
7305 newSizeY = HIWORD(lParam);
\r
7306 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7307 RECT rectText, rectInput;
\r
7309 int newTextHeight, newTextWidth;
\r
7310 GetWindowRect(hText, &rectText);
\r
7311 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7312 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7313 if (newTextHeight < 0) {
\r
7314 newSizeY += -newTextHeight;
\r
7315 newTextHeight = 0;
\r
7317 SetWindowPos(hText, NULL, 0, 0,
\r
7318 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7319 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7320 pt.x = rectInput.left;
\r
7321 pt.y = rectInput.top + newSizeY - sizeY;
\r
7322 ScreenToClient(hDlg, &pt);
\r
7323 SetWindowPos(hInput, NULL,
\r
7324 pt.x, pt.y, /* needs client coords */
\r
7325 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7326 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7332 case WM_GETMINMAXINFO:
\r
7333 /* Prevent resizing window too small */
\r
7334 mmi = (MINMAXINFO *) lParam;
\r
7335 mmi->ptMinTrackSize.x = 100;
\r
7336 mmi->ptMinTrackSize.y = 100;
\r
7339 /* [AS] Snapping */
\r
7340 case WM_ENTERSIZEMOVE:
\r
7341 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7344 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7347 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7349 case WM_EXITSIZEMOVE:
\r
7350 UpdateICSWidth(hText);
\r
7351 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7354 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7362 if (hwndConsole) return;
\r
7363 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7364 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7369 ConsoleOutput(char* data, int length, int forceVisible)
\r
7374 char buf[CO_MAX+1];
\r
7377 static int delayLF = 0;
\r
7378 CHARRANGE savesel, sel;
\r
7380 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7388 while (length--) {
\r
7396 } else if (*p == '\007') {
\r
7397 MyPlaySound(&sounds[(int)SoundBell]);
\r
7404 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7405 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7406 /* Save current selection */
\r
7407 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7408 exlen = GetWindowTextLength(hText);
\r
7409 /* Find out whether current end of text is visible */
\r
7410 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7411 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7412 /* Trim existing text if it's too long */
\r
7413 if (exlen + (q - buf) > CO_MAX) {
\r
7414 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7417 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7418 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7420 savesel.cpMin -= trim;
\r
7421 savesel.cpMax -= trim;
\r
7422 if (exlen < 0) exlen = 0;
\r
7423 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7424 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7426 /* Append the new text */
\r
7427 sel.cpMin = exlen;
\r
7428 sel.cpMax = exlen;
\r
7429 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7430 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7431 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7432 if (forceVisible || exlen == 0 ||
\r
7433 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7434 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7435 /* Scroll to make new end of text visible if old end of text
\r
7436 was visible or new text is an echo of user typein */
\r
7437 sel.cpMin = 9999999;
\r
7438 sel.cpMax = 9999999;
\r
7439 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7440 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7441 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7442 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7444 if (savesel.cpMax == exlen || forceVisible) {
\r
7445 /* Move insert point to new end of text if it was at the old
\r
7446 end of text or if the new text is an echo of user typein */
\r
7447 sel.cpMin = 9999999;
\r
7448 sel.cpMax = 9999999;
\r
7449 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7451 /* Restore previous selection */
\r
7452 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7454 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7461 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7465 COLORREF oldFg, oldBg;
\r
7469 if(copyNumber > 1)
\r
7470 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7472 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7473 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7474 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7477 rect.right = x + squareSize;
\r
7479 rect.bottom = y + squareSize;
\r
7482 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7483 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7484 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7485 &rect, str, strlen(str), NULL);
\r
7487 (void) SetTextColor(hdc, oldFg);
\r
7488 (void) SetBkColor(hdc, oldBg);
\r
7489 (void) SelectObject(hdc, oldFont);
\r
7493 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7494 RECT *rect, char *color, char *flagFell)
\r
7498 COLORREF oldFg, oldBg;
\r
7501 if (twoBoards && partnerUp) return;
\r
7502 if (appData.clockMode) {
\r
7504 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7506 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7513 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7514 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7516 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7517 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7519 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7523 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7524 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7525 rect, str, strlen(str), NULL);
\r
7526 if(logoHeight > 0 && appData.clockMode) {
\r
7528 str += strlen(color)+2;
\r
7529 r.top = rect->top + logoHeight/2;
\r
7530 r.left = rect->left;
\r
7531 r.right = rect->right;
\r
7532 r.bottom = rect->bottom;
\r
7533 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7534 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7535 &r, str, strlen(str), NULL);
\r
7537 (void) SetTextColor(hdc, oldFg);
\r
7538 (void) SetBkColor(hdc, oldBg);
\r
7539 (void) SelectObject(hdc, oldFont);
\r
7544 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7550 if( count <= 0 ) {
\r
7551 if (appData.debugMode) {
\r
7552 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7555 return ERROR_INVALID_USER_BUFFER;
\r
7558 ResetEvent(ovl->hEvent);
\r
7559 ovl->Offset = ovl->OffsetHigh = 0;
\r
7560 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7564 err = GetLastError();
\r
7565 if (err == ERROR_IO_PENDING) {
\r
7566 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7570 err = GetLastError();
\r
7577 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7582 ResetEvent(ovl->hEvent);
\r
7583 ovl->Offset = ovl->OffsetHigh = 0;
\r
7584 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7588 err = GetLastError();
\r
7589 if (err == ERROR_IO_PENDING) {
\r
7590 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7594 err = GetLastError();
\r
7600 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7601 void CheckForInputBufferFull( InputSource * is )
\r
7603 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7604 /* Look for end of line */
\r
7605 char * p = is->buf;
\r
7607 while( p < is->next && *p != '\n' ) {
\r
7611 if( p >= is->next ) {
\r
7612 if (appData.debugMode) {
\r
7613 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7616 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7617 is->count = (DWORD) -1;
\r
7618 is->next = is->buf;
\r
7624 InputThread(LPVOID arg)
\r
7629 is = (InputSource *) arg;
\r
7630 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7631 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7632 while (is->hThread != NULL) {
\r
7633 is->error = DoReadFile(is->hFile, is->next,
\r
7634 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7635 &is->count, &ovl);
\r
7636 if (is->error == NO_ERROR) {
\r
7637 is->next += is->count;
\r
7639 if (is->error == ERROR_BROKEN_PIPE) {
\r
7640 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7643 is->count = (DWORD) -1;
\r
7644 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7649 CheckForInputBufferFull( is );
\r
7651 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7653 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7655 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7658 CloseHandle(ovl.hEvent);
\r
7659 CloseHandle(is->hFile);
\r
7661 if (appData.debugMode) {
\r
7662 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7669 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7671 NonOvlInputThread(LPVOID arg)
\r
7678 is = (InputSource *) arg;
\r
7679 while (is->hThread != NULL) {
\r
7680 is->error = ReadFile(is->hFile, is->next,
\r
7681 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7682 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7683 if (is->error == NO_ERROR) {
\r
7684 /* Change CRLF to LF */
\r
7685 if (is->next > is->buf) {
\r
7687 i = is->count + 1;
\r
7695 if (prev == '\r' && *p == '\n') {
\r
7707 if (is->error == ERROR_BROKEN_PIPE) {
\r
7708 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7711 is->count = (DWORD) -1;
\r
7715 CheckForInputBufferFull( is );
\r
7717 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7719 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7721 if (is->count < 0) break; /* Quit on error */
\r
7723 CloseHandle(is->hFile);
\r
7728 SocketInputThread(LPVOID arg)
\r
7732 is = (InputSource *) arg;
\r
7733 while (is->hThread != NULL) {
\r
7734 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7735 if ((int)is->count == SOCKET_ERROR) {
\r
7736 is->count = (DWORD) -1;
\r
7737 is->error = WSAGetLastError();
\r
7739 is->error = NO_ERROR;
\r
7740 is->next += is->count;
\r
7741 if (is->count == 0 && is->second == is) {
\r
7742 /* End of file on stderr; quit with no message */
\r
7746 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7748 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7750 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7756 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7760 is = (InputSource *) lParam;
\r
7761 if (is->lineByLine) {
\r
7762 /* Feed in lines one by one */
\r
7763 char *p = is->buf;
\r
7765 while (q < is->next) {
\r
7766 if (*q++ == '\n') {
\r
7767 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7772 /* Move any partial line to the start of the buffer */
\r
7774 while (p < is->next) {
\r
7779 if (is->error != NO_ERROR || is->count == 0) {
\r
7780 /* Notify backend of the error. Note: If there was a partial
\r
7781 line at the end, it is not flushed through. */
\r
7782 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7785 /* Feed in the whole chunk of input at once */
\r
7786 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7787 is->next = is->buf;
\r
7791 /*---------------------------------------------------------------------------*\
\r
7793 * Menu enables. Used when setting various modes.
\r
7795 \*---------------------------------------------------------------------------*/
\r
7803 GreyRevert(Boolean grey)
\r
7804 { // [HGM] vari: for retracting variations in local mode
\r
7805 HMENU hmenu = GetMenu(hwndMain);
\r
7806 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7807 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7811 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7813 while (enab->item > 0) {
\r
7814 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7819 Enables gnuEnables[] = {
\r
7820 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7834 // Needed to switch from ncp to GNU mode on Engine Load
\r
7835 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7836 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7837 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7838 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7840 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7841 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7842 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7843 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7844 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7846 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7847 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7848 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7852 Enables icsEnables[] = {
\r
7853 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7877 Enables zippyEnables[] = {
\r
7878 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7886 Enables ncpEnables[] = {
\r
7887 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7897 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7912 Enables trainingOnEnables[] = {
\r
7913 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7925 Enables trainingOffEnables[] = {
\r
7926 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7938 /* These modify either ncpEnables or gnuEnables */
\r
7939 Enables cmailEnables[] = {
\r
7940 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7941 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7942 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7943 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7945 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7950 Enables machineThinkingEnables[] = {
\r
7951 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7964 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7970 Enables userThinkingEnables[] = {
\r
7971 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7972 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7973 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7975 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7976 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7977 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7978 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7981 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7982 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7983 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7984 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7985 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7986 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7990 /*---------------------------------------------------------------------------*\
\r
7992 * Front-end interface functions exported by XBoard.
\r
7993 * Functions appear in same order as prototypes in frontend.h.
\r
7995 \*---------------------------------------------------------------------------*/
\r
7997 CheckMark(UINT item, int state)
\r
7999 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8005 static UINT prevChecked = 0;
\r
8006 static int prevPausing = 0;
\r
8009 if (pausing != prevPausing) {
\r
8010 prevPausing = pausing;
\r
8011 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8012 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8013 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8016 switch (gameMode) {
\r
8017 case BeginningOfGame:
\r
8018 if (appData.icsActive)
\r
8019 nowChecked = IDM_IcsClient;
\r
8020 else if (appData.noChessProgram)
\r
8021 nowChecked = IDM_EditGame;
\r
8023 nowChecked = IDM_MachineBlack;
\r
8025 case MachinePlaysBlack:
\r
8026 nowChecked = IDM_MachineBlack;
\r
8028 case MachinePlaysWhite:
\r
8029 nowChecked = IDM_MachineWhite;
\r
8031 case TwoMachinesPlay:
\r
8032 nowChecked = IDM_TwoMachines;
\r
8035 nowChecked = IDM_AnalysisMode;
\r
8038 nowChecked = IDM_AnalyzeFile;
\r
8041 nowChecked = IDM_EditGame;
\r
8043 case PlayFromGameFile:
\r
8044 nowChecked = IDM_LoadGame;
\r
8046 case EditPosition:
\r
8047 nowChecked = IDM_EditPosition;
\r
8050 nowChecked = IDM_Training;
\r
8052 case IcsPlayingWhite:
\r
8053 case IcsPlayingBlack:
\r
8054 case IcsObserving:
\r
8056 nowChecked = IDM_IcsClient;
\r
8063 CheckMark(prevChecked, MF_UNCHECKED);
\r
8064 CheckMark(nowChecked, MF_CHECKED);
\r
8065 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8067 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8068 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8069 MF_BYCOMMAND|MF_ENABLED);
\r
8071 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8072 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8075 prevChecked = nowChecked;
\r
8077 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8078 if (appData.icsActive) {
\r
8079 if (appData.icsEngineAnalyze) {
\r
8080 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8082 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8085 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8091 HMENU hmenu = GetMenu(hwndMain);
\r
8092 SetMenuEnables(hmenu, icsEnables);
\r
8093 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8094 MF_BYCOMMAND|MF_ENABLED);
\r
8096 if (appData.zippyPlay) {
\r
8097 SetMenuEnables(hmenu, zippyEnables);
\r
8098 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8099 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8100 MF_BYCOMMAND|MF_ENABLED);
\r
8108 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8114 HMENU hmenu = GetMenu(hwndMain);
\r
8115 SetMenuEnables(hmenu, ncpEnables);
\r
8116 DrawMenuBar(hwndMain);
\r
8122 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8126 SetTrainingModeOn()
\r
8129 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8130 for (i = 0; i < N_BUTTONS; i++) {
\r
8131 if (buttonDesc[i].hwnd != NULL)
\r
8132 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8137 VOID SetTrainingModeOff()
\r
8140 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8141 for (i = 0; i < N_BUTTONS; i++) {
\r
8142 if (buttonDesc[i].hwnd != NULL)
\r
8143 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8149 SetUserThinkingEnables()
\r
8151 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8155 SetMachineThinkingEnables()
\r
8157 HMENU hMenu = GetMenu(hwndMain);
\r
8158 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8160 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8162 if (gameMode == MachinePlaysBlack) {
\r
8163 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8164 } else if (gameMode == MachinePlaysWhite) {
\r
8165 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8166 } else if (gameMode == TwoMachinesPlay) {
\r
8167 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8173 DisplayTitle(char *str)
\r
8175 char title[MSG_SIZ], *host;
\r
8176 if (str[0] != NULLCHAR) {
\r
8177 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8178 } else if (appData.icsActive) {
\r
8179 if (appData.icsCommPort[0] != NULLCHAR)
\r
8182 host = appData.icsHost;
\r
8183 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8184 } else if (appData.noChessProgram) {
\r
8185 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8187 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8188 strcat(title, ": ");
\r
8189 strcat(title, first.tidy);
\r
8191 SetWindowText(hwndMain, title);
\r
8196 DisplayMessage(char *str1, char *str2)
\r
8200 int remain = MESSAGE_TEXT_MAX - 1;
\r
8203 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8204 messageText[0] = NULLCHAR;
\r
8206 len = strlen(str1);
\r
8207 if (len > remain) len = remain;
\r
8208 strncpy(messageText, str1, len);
\r
8209 messageText[len] = NULLCHAR;
\r
8212 if (*str2 && remain >= 2) {
\r
8214 strcat(messageText, " ");
\r
8217 len = strlen(str2);
\r
8218 if (len > remain) len = remain;
\r
8219 strncat(messageText, str2, len);
\r
8221 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8222 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8224 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8228 hdc = GetDC(hwndMain);
\r
8229 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8230 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8231 &messageRect, messageText, strlen(messageText), NULL);
\r
8232 (void) SelectObject(hdc, oldFont);
\r
8233 (void) ReleaseDC(hwndMain, hdc);
\r
8237 DisplayError(char *str, int error)
\r
8239 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8243 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8245 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8246 NULL, error, LANG_NEUTRAL,
\r
8247 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8249 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8251 ErrorMap *em = errmap;
\r
8252 while (em->err != 0 && em->err != error) em++;
\r
8253 if (em->err != 0) {
\r
8254 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8256 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8261 ErrorPopUp(_("Error"), buf);
\r
8266 DisplayMoveError(char *str)
\r
8268 fromX = fromY = -1;
\r
8269 ClearHighlights();
\r
8270 DrawPosition(FALSE, NULL);
\r
8271 if (appData.popupMoveErrors) {
\r
8272 ErrorPopUp(_("Error"), str);
\r
8274 DisplayMessage(str, "");
\r
8275 moveErrorMessageUp = TRUE;
\r
8280 DisplayFatalError(char *str, int error, int exitStatus)
\r
8282 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8284 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8287 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8288 NULL, error, LANG_NEUTRAL,
\r
8289 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8291 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8293 ErrorMap *em = errmap;
\r
8294 while (em->err != 0 && em->err != error) em++;
\r
8295 if (em->err != 0) {
\r
8296 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8298 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8303 if (appData.debugMode) {
\r
8304 fprintf(debugFP, "%s: %s\n", label, str);
\r
8306 if (appData.popupExitMessage) {
\r
8307 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8308 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8310 ExitEvent(exitStatus);
\r
8315 DisplayInformation(char *str)
\r
8317 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8322 DisplayNote(char *str)
\r
8324 ErrorPopUp(_("Note"), str);
\r
8329 char *title, *question, *replyPrefix;
\r
8334 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8336 static QuestionParams *qp;
\r
8337 char reply[MSG_SIZ];
\r
8340 switch (message) {
\r
8341 case WM_INITDIALOG:
\r
8342 qp = (QuestionParams *) lParam;
\r
8343 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8344 Translate(hDlg, DLG_Question);
\r
8345 SetWindowText(hDlg, qp->title);
\r
8346 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8347 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8351 switch (LOWORD(wParam)) {
\r
8353 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8354 if (*reply) strcat(reply, " ");
\r
8355 len = strlen(reply);
\r
8356 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8357 strcat(reply, "\n");
\r
8358 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8359 EndDialog(hDlg, TRUE);
\r
8360 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8363 EndDialog(hDlg, FALSE);
\r
8374 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8376 QuestionParams qp;
\r
8380 qp.question = question;
\r
8381 qp.replyPrefix = replyPrefix;
\r
8383 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8384 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8385 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8386 FreeProcInstance(lpProc);
\r
8389 /* [AS] Pick FRC position */
\r
8390 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8392 static int * lpIndexFRC;
\r
8398 case WM_INITDIALOG:
\r
8399 lpIndexFRC = (int *) lParam;
\r
8401 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8402 Translate(hDlg, DLG_NewGameFRC);
\r
8404 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8405 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8406 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8407 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8412 switch( LOWORD(wParam) ) {
\r
8414 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8415 EndDialog( hDlg, 0 );
\r
8416 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8419 EndDialog( hDlg, 1 );
\r
8421 case IDC_NFG_Edit:
\r
8422 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8423 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8425 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8428 case IDC_NFG_Random:
\r
8429 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8430 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8443 int index = appData.defaultFrcPosition;
\r
8444 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8446 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8448 if( result == 0 ) {
\r
8449 appData.defaultFrcPosition = index;
\r
8455 /* [AS] Game list options. Refactored by HGM */
\r
8457 HWND gameListOptionsDialog;
\r
8459 // low-level front-end: clear text edit / list widget
\r
8463 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8466 // low-level front-end: clear text edit / list widget
\r
8468 GLT_DeSelectList()
\r
8470 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8473 // low-level front-end: append line to text edit / list widget
\r
8475 GLT_AddToList( char *name )
\r
8478 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8482 // low-level front-end: get line from text edit / list widget
\r
8484 GLT_GetFromList( int index, char *name )
\r
8487 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8493 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8495 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8496 int idx2 = idx1 + delta;
\r
8497 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8499 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8502 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8503 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8504 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8505 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8509 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8513 case WM_INITDIALOG:
\r
8514 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8516 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8517 Translate(hDlg, DLG_GameListOptions);
\r
8519 /* Initialize list */
\r
8520 GLT_TagsToList( lpUserGLT );
\r
8522 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8527 switch( LOWORD(wParam) ) {
\r
8530 EndDialog( hDlg, 0 );
\r
8533 EndDialog( hDlg, 1 );
\r
8536 case IDC_GLT_Default:
\r
8537 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8540 case IDC_GLT_Restore:
\r
8541 GLT_TagsToList( appData.gameListTags );
\r
8545 GLT_MoveSelection( hDlg, -1 );
\r
8548 case IDC_GLT_Down:
\r
8549 GLT_MoveSelection( hDlg, +1 );
\r
8559 int GameListOptions()
\r
8562 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8564 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8566 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8568 if( result == 0 ) {
\r
8569 /* [AS] Memory leak here! */
\r
8570 appData.gameListTags = strdup( lpUserGLT );
\r
8577 DisplayIcsInteractionTitle(char *str)
\r
8579 char consoleTitle[MSG_SIZ];
\r
8581 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8582 SetWindowText(hwndConsole, consoleTitle);
\r
8584 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8585 char buf[MSG_SIZ], *p = buf, *q;
\r
8586 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8588 q = strchr(p, ';');
\r
8590 if(*p) ChatPopUp(p);
\r
8594 SetActiveWindow(hwndMain);
\r
8598 DrawPosition(int fullRedraw, Board board)
\r
8600 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8603 void NotifyFrontendLogin()
\r
8606 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8612 fromX = fromY = -1;
\r
8613 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8614 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8615 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8616 dragInfo.lastpos = dragInfo.pos;
\r
8617 dragInfo.start.x = dragInfo.start.y = -1;
\r
8618 dragInfo.from = dragInfo.start;
\r
8620 DrawPosition(TRUE, NULL);
\r
8627 CommentPopUp(char *title, char *str)
\r
8629 HWND hwnd = GetActiveWindow();
\r
8630 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8632 SetActiveWindow(hwnd);
\r
8636 CommentPopDown(void)
\r
8638 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8639 if (commentDialog) {
\r
8640 ShowWindow(commentDialog, SW_HIDE);
\r
8642 commentUp = FALSE;
\r
8646 EditCommentPopUp(int index, char *title, char *str)
\r
8648 EitherCommentPopUp(index, title, str, TRUE);
\r
8655 MyPlaySound(&sounds[(int)SoundMove]);
\r
8658 VOID PlayIcsWinSound()
\r
8660 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8663 VOID PlayIcsLossSound()
\r
8665 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8668 VOID PlayIcsDrawSound()
\r
8670 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8673 VOID PlayIcsUnfinishedSound()
\r
8675 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8681 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8687 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8695 consoleEcho = TRUE;
\r
8696 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8697 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8698 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8707 consoleEcho = FALSE;
\r
8708 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8709 /* This works OK: set text and background both to the same color */
\r
8711 cf.crTextColor = COLOR_ECHOOFF;
\r
8712 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8713 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8716 /* No Raw()...? */
\r
8718 void Colorize(ColorClass cc, int continuation)
\r
8720 currentColorClass = cc;
\r
8721 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8722 consoleCF.crTextColor = textAttribs[cc].color;
\r
8723 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8724 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8730 static char buf[MSG_SIZ];
\r
8731 DWORD bufsiz = MSG_SIZ;
\r
8733 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8734 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8736 if (!GetUserName(buf, &bufsiz)) {
\r
8737 /*DisplayError("Error getting user name", GetLastError());*/
\r
8738 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8746 static char buf[MSG_SIZ];
\r
8747 DWORD bufsiz = MSG_SIZ;
\r
8749 if (!GetComputerName(buf, &bufsiz)) {
\r
8750 /*DisplayError("Error getting host name", GetLastError());*/
\r
8751 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8758 ClockTimerRunning()
\r
8760 return clockTimerEvent != 0;
\r
8766 if (clockTimerEvent == 0) return FALSE;
\r
8767 KillTimer(hwndMain, clockTimerEvent);
\r
8768 clockTimerEvent = 0;
\r
8773 StartClockTimer(long millisec)
\r
8775 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8776 (UINT) millisec, NULL);
\r
8780 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8783 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8785 if(appData.noGUI) return;
\r
8786 hdc = GetDC(hwndMain);
\r
8787 if (!IsIconic(hwndMain)) {
\r
8788 DisplayAClock(hdc, timeRemaining, highlight,
\r
8789 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8791 if (highlight && iconCurrent == iconBlack) {
\r
8792 iconCurrent = iconWhite;
\r
8793 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8794 if (IsIconic(hwndMain)) {
\r
8795 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8798 (void) ReleaseDC(hwndMain, hdc);
\r
8800 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8804 DisplayBlackClock(long timeRemaining, int highlight)
\r
8807 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8809 if(appData.noGUI) return;
\r
8810 hdc = GetDC(hwndMain);
\r
8811 if (!IsIconic(hwndMain)) {
\r
8812 DisplayAClock(hdc, timeRemaining, highlight,
\r
8813 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8815 if (highlight && iconCurrent == iconWhite) {
\r
8816 iconCurrent = iconBlack;
\r
8817 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8818 if (IsIconic(hwndMain)) {
\r
8819 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8822 (void) ReleaseDC(hwndMain, hdc);
\r
8824 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8829 LoadGameTimerRunning()
\r
8831 return loadGameTimerEvent != 0;
\r
8835 StopLoadGameTimer()
\r
8837 if (loadGameTimerEvent == 0) return FALSE;
\r
8838 KillTimer(hwndMain, loadGameTimerEvent);
\r
8839 loadGameTimerEvent = 0;
\r
8844 StartLoadGameTimer(long millisec)
\r
8846 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8847 (UINT) millisec, NULL);
\r
8855 char fileTitle[MSG_SIZ];
\r
8857 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8858 f = OpenFileDialog(hwndMain, "a", defName,
\r
8859 appData.oldSaveStyle ? "gam" : "pgn",
\r
8861 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8863 SaveGame(f, 0, "");
\r
8870 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8872 if (delayedTimerEvent != 0) {
\r
8873 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8874 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8876 KillTimer(hwndMain, delayedTimerEvent);
\r
8877 delayedTimerEvent = 0;
\r
8878 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8879 delayedTimerCallback();
\r
8881 delayedTimerCallback = cb;
\r
8882 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8883 (UINT) millisec, NULL);
\r
8886 DelayedEventCallback
\r
8889 if (delayedTimerEvent) {
\r
8890 return delayedTimerCallback;
\r
8897 CancelDelayedEvent()
\r
8899 if (delayedTimerEvent) {
\r
8900 KillTimer(hwndMain, delayedTimerEvent);
\r
8901 delayedTimerEvent = 0;
\r
8905 DWORD GetWin32Priority(int nice)
\r
8906 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8908 REALTIME_PRIORITY_CLASS 0x00000100
\r
8909 HIGH_PRIORITY_CLASS 0x00000080
\r
8910 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8911 NORMAL_PRIORITY_CLASS 0x00000020
\r
8912 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8913 IDLE_PRIORITY_CLASS 0x00000040
\r
8915 if (nice < -15) return 0x00000080;
\r
8916 if (nice < 0) return 0x00008000;
\r
8917 if (nice == 0) return 0x00000020;
\r
8918 if (nice < 15) return 0x00004000;
\r
8919 return 0x00000040;
\r
8922 void RunCommand(char *cmdLine)
\r
8924 /* Now create the child process. */
\r
8925 STARTUPINFO siStartInfo;
\r
8926 PROCESS_INFORMATION piProcInfo;
\r
8928 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8929 siStartInfo.lpReserved = NULL;
\r
8930 siStartInfo.lpDesktop = NULL;
\r
8931 siStartInfo.lpTitle = NULL;
\r
8932 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8933 siStartInfo.cbReserved2 = 0;
\r
8934 siStartInfo.lpReserved2 = NULL;
\r
8935 siStartInfo.hStdInput = NULL;
\r
8936 siStartInfo.hStdOutput = NULL;
\r
8937 siStartInfo.hStdError = NULL;
\r
8939 CreateProcess(NULL,
\r
8940 cmdLine, /* command line */
\r
8941 NULL, /* process security attributes */
\r
8942 NULL, /* primary thread security attrs */
\r
8943 TRUE, /* handles are inherited */
\r
8944 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8945 NULL, /* use parent's environment */
\r
8947 &siStartInfo, /* STARTUPINFO pointer */
\r
8948 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8950 CloseHandle(piProcInfo.hThread);
\r
8953 /* Start a child process running the given program.
\r
8954 The process's standard output can be read from "from", and its
\r
8955 standard input can be written to "to".
\r
8956 Exit with fatal error if anything goes wrong.
\r
8957 Returns an opaque pointer that can be used to destroy the process
\r
8961 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8963 #define BUFSIZE 4096
\r
8965 HANDLE hChildStdinRd, hChildStdinWr,
\r
8966 hChildStdoutRd, hChildStdoutWr;
\r
8967 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8968 SECURITY_ATTRIBUTES saAttr;
\r
8970 PROCESS_INFORMATION piProcInfo;
\r
8971 STARTUPINFO siStartInfo;
\r
8973 char buf[MSG_SIZ];
\r
8976 if (appData.debugMode) {
\r
8977 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8982 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8983 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8984 saAttr.bInheritHandle = TRUE;
\r
8985 saAttr.lpSecurityDescriptor = NULL;
\r
8988 * The steps for redirecting child's STDOUT:
\r
8989 * 1. Create anonymous pipe to be STDOUT for child.
\r
8990 * 2. Create a noninheritable duplicate of read handle,
\r
8991 * and close the inheritable read handle.
\r
8994 /* Create a pipe for the child's STDOUT. */
\r
8995 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8996 return GetLastError();
\r
8999 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9000 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9001 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9002 FALSE, /* not inherited */
\r
9003 DUPLICATE_SAME_ACCESS);
\r
9005 return GetLastError();
\r
9007 CloseHandle(hChildStdoutRd);
\r
9010 * The steps for redirecting child's STDIN:
\r
9011 * 1. Create anonymous pipe to be STDIN for child.
\r
9012 * 2. Create a noninheritable duplicate of write handle,
\r
9013 * and close the inheritable write handle.
\r
9016 /* Create a pipe for the child's STDIN. */
\r
9017 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9018 return GetLastError();
\r
9021 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9022 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9023 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9024 FALSE, /* not inherited */
\r
9025 DUPLICATE_SAME_ACCESS);
\r
9027 return GetLastError();
\r
9029 CloseHandle(hChildStdinWr);
\r
9031 /* Arrange to (1) look in dir for the child .exe file, and
\r
9032 * (2) have dir be the child's working directory. Interpret
\r
9033 * dir relative to the directory WinBoard loaded from. */
\r
9034 GetCurrentDirectory(MSG_SIZ, buf);
\r
9035 SetCurrentDirectory(installDir);
\r
9036 SetCurrentDirectory(dir);
\r
9038 /* Now create the child process. */
\r
9040 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9041 siStartInfo.lpReserved = NULL;
\r
9042 siStartInfo.lpDesktop = NULL;
\r
9043 siStartInfo.lpTitle = NULL;
\r
9044 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9045 siStartInfo.cbReserved2 = 0;
\r
9046 siStartInfo.lpReserved2 = NULL;
\r
9047 siStartInfo.hStdInput = hChildStdinRd;
\r
9048 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9049 siStartInfo.hStdError = hChildStdoutWr;
\r
9051 fSuccess = CreateProcess(NULL,
\r
9052 cmdLine, /* command line */
\r
9053 NULL, /* process security attributes */
\r
9054 NULL, /* primary thread security attrs */
\r
9055 TRUE, /* handles are inherited */
\r
9056 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9057 NULL, /* use parent's environment */
\r
9059 &siStartInfo, /* STARTUPINFO pointer */
\r
9060 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9062 err = GetLastError();
\r
9063 SetCurrentDirectory(buf); /* return to prev directory */
\r
9068 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9069 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9070 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9073 /* Close the handles we don't need in the parent */
\r
9074 CloseHandle(piProcInfo.hThread);
\r
9075 CloseHandle(hChildStdinRd);
\r
9076 CloseHandle(hChildStdoutWr);
\r
9078 /* Prepare return value */
\r
9079 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9080 cp->kind = CPReal;
\r
9081 cp->hProcess = piProcInfo.hProcess;
\r
9082 cp->pid = piProcInfo.dwProcessId;
\r
9083 cp->hFrom = hChildStdoutRdDup;
\r
9084 cp->hTo = hChildStdinWrDup;
\r
9086 *pr = (void *) cp;
\r
9088 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9089 2000 where engines sometimes don't see the initial command(s)
\r
9090 from WinBoard and hang. I don't understand how that can happen,
\r
9091 but the Sleep is harmless, so I've put it in. Others have also
\r
9092 reported what may be the same problem, so hopefully this will fix
\r
9093 it for them too. */
\r
9101 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9103 ChildProc *cp; int result;
\r
9105 cp = (ChildProc *) pr;
\r
9106 if (cp == NULL) return;
\r
9108 switch (cp->kind) {
\r
9110 /* TerminateProcess is considered harmful, so... */
\r
9111 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9112 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9113 /* The following doesn't work because the chess program
\r
9114 doesn't "have the same console" as WinBoard. Maybe
\r
9115 we could arrange for this even though neither WinBoard
\r
9116 nor the chess program uses a console for stdio? */
\r
9117 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9119 /* [AS] Special termination modes for misbehaving programs... */
\r
9120 if( signal == 9 ) {
\r
9121 result = TerminateProcess( cp->hProcess, 0 );
\r
9123 if ( appData.debugMode) {
\r
9124 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9127 else if( signal == 10 ) {
\r
9128 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9130 if( dw != WAIT_OBJECT_0 ) {
\r
9131 result = TerminateProcess( cp->hProcess, 0 );
\r
9133 if ( appData.debugMode) {
\r
9134 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9140 CloseHandle(cp->hProcess);
\r
9144 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9148 closesocket(cp->sock);
\r
9153 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9154 closesocket(cp->sock);
\r
9155 closesocket(cp->sock2);
\r
9163 InterruptChildProcess(ProcRef pr)
\r
9167 cp = (ChildProc *) pr;
\r
9168 if (cp == NULL) return;
\r
9169 switch (cp->kind) {
\r
9171 /* The following doesn't work because the chess program
\r
9172 doesn't "have the same console" as WinBoard. Maybe
\r
9173 we could arrange for this even though neither WinBoard
\r
9174 nor the chess program uses a console for stdio */
\r
9175 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9180 /* Can't interrupt */
\r
9184 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9191 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9193 char cmdLine[MSG_SIZ];
\r
9195 if (port[0] == NULLCHAR) {
\r
9196 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9198 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9200 return StartChildProcess(cmdLine, "", pr);
\r
9204 /* Code to open TCP sockets */
\r
9207 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9213 struct sockaddr_in sa, mysa;
\r
9214 struct hostent FAR *hp;
\r
9215 unsigned short uport;
\r
9216 WORD wVersionRequested;
\r
9219 /* Initialize socket DLL */
\r
9220 wVersionRequested = MAKEWORD(1, 1);
\r
9221 err = WSAStartup(wVersionRequested, &wsaData);
\r
9222 if (err != 0) return err;
\r
9225 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9226 err = WSAGetLastError();
\r
9231 /* Bind local address using (mostly) don't-care values.
\r
9233 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9234 mysa.sin_family = AF_INET;
\r
9235 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9236 uport = (unsigned short) 0;
\r
9237 mysa.sin_port = htons(uport);
\r
9238 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9239 == SOCKET_ERROR) {
\r
9240 err = WSAGetLastError();
\r
9245 /* Resolve remote host name */
\r
9246 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9247 if (!(hp = gethostbyname(host))) {
\r
9248 unsigned int b0, b1, b2, b3;
\r
9250 err = WSAGetLastError();
\r
9252 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9253 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9254 hp->h_addrtype = AF_INET;
\r
9256 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9257 hp->h_addr_list[0] = (char *) malloc(4);
\r
9258 hp->h_addr_list[0][0] = (char) b0;
\r
9259 hp->h_addr_list[0][1] = (char) b1;
\r
9260 hp->h_addr_list[0][2] = (char) b2;
\r
9261 hp->h_addr_list[0][3] = (char) b3;
\r
9267 sa.sin_family = hp->h_addrtype;
\r
9268 uport = (unsigned short) atoi(port);
\r
9269 sa.sin_port = htons(uport);
\r
9270 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9272 /* Make connection */
\r
9273 if (connect(s, (struct sockaddr *) &sa,
\r
9274 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9275 err = WSAGetLastError();
\r
9280 /* Prepare return value */
\r
9281 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9282 cp->kind = CPSock;
\r
9284 *pr = (ProcRef *) cp;
\r
9290 OpenCommPort(char *name, ProcRef *pr)
\r
9295 char fullname[MSG_SIZ];
\r
9297 if (*name != '\\')
\r
9298 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9300 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9302 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9303 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9304 if (h == (HANDLE) -1) {
\r
9305 return GetLastError();
\r
9309 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9311 /* Accumulate characters until a 100ms pause, then parse */
\r
9312 ct.ReadIntervalTimeout = 100;
\r
9313 ct.ReadTotalTimeoutMultiplier = 0;
\r
9314 ct.ReadTotalTimeoutConstant = 0;
\r
9315 ct.WriteTotalTimeoutMultiplier = 0;
\r
9316 ct.WriteTotalTimeoutConstant = 0;
\r
9317 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9319 /* Prepare return value */
\r
9320 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9321 cp->kind = CPComm;
\r
9324 *pr = (ProcRef *) cp;
\r
9330 OpenLoopback(ProcRef *pr)
\r
9332 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9338 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9343 struct sockaddr_in sa, mysa;
\r
9344 struct hostent FAR *hp;
\r
9345 unsigned short uport;
\r
9346 WORD wVersionRequested;
\r
9349 char stderrPortStr[MSG_SIZ];
\r
9351 /* Initialize socket DLL */
\r
9352 wVersionRequested = MAKEWORD(1, 1);
\r
9353 err = WSAStartup(wVersionRequested, &wsaData);
\r
9354 if (err != 0) return err;
\r
9356 /* Resolve remote host name */
\r
9357 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9358 if (!(hp = gethostbyname(host))) {
\r
9359 unsigned int b0, b1, b2, b3;
\r
9361 err = WSAGetLastError();
\r
9363 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9364 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9365 hp->h_addrtype = AF_INET;
\r
9367 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9368 hp->h_addr_list[0] = (char *) malloc(4);
\r
9369 hp->h_addr_list[0][0] = (char) b0;
\r
9370 hp->h_addr_list[0][1] = (char) b1;
\r
9371 hp->h_addr_list[0][2] = (char) b2;
\r
9372 hp->h_addr_list[0][3] = (char) b3;
\r
9378 sa.sin_family = hp->h_addrtype;
\r
9379 uport = (unsigned short) 514;
\r
9380 sa.sin_port = htons(uport);
\r
9381 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9383 /* Bind local socket to unused "privileged" port address
\r
9385 s = INVALID_SOCKET;
\r
9386 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9387 mysa.sin_family = AF_INET;
\r
9388 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9389 for (fromPort = 1023;; fromPort--) {
\r
9390 if (fromPort < 0) {
\r
9392 return WSAEADDRINUSE;
\r
9394 if (s == INVALID_SOCKET) {
\r
9395 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9396 err = WSAGetLastError();
\r
9401 uport = (unsigned short) fromPort;
\r
9402 mysa.sin_port = htons(uport);
\r
9403 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9404 == SOCKET_ERROR) {
\r
9405 err = WSAGetLastError();
\r
9406 if (err == WSAEADDRINUSE) continue;
\r
9410 if (connect(s, (struct sockaddr *) &sa,
\r
9411 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9412 err = WSAGetLastError();
\r
9413 if (err == WSAEADDRINUSE) {
\r
9424 /* Bind stderr local socket to unused "privileged" port address
\r
9426 s2 = INVALID_SOCKET;
\r
9427 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9428 mysa.sin_family = AF_INET;
\r
9429 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9430 for (fromPort = 1023;; fromPort--) {
\r
9431 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9432 if (fromPort < 0) {
\r
9433 (void) closesocket(s);
\r
9435 return WSAEADDRINUSE;
\r
9437 if (s2 == INVALID_SOCKET) {
\r
9438 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9439 err = WSAGetLastError();
\r
9445 uport = (unsigned short) fromPort;
\r
9446 mysa.sin_port = htons(uport);
\r
9447 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9448 == SOCKET_ERROR) {
\r
9449 err = WSAGetLastError();
\r
9450 if (err == WSAEADDRINUSE) continue;
\r
9451 (void) closesocket(s);
\r
9455 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9456 err = WSAGetLastError();
\r
9457 if (err == WSAEADDRINUSE) {
\r
9459 s2 = INVALID_SOCKET;
\r
9462 (void) closesocket(s);
\r
9463 (void) closesocket(s2);
\r
9469 prevStderrPort = fromPort; // remember port used
\r
9470 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9472 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9473 err = WSAGetLastError();
\r
9474 (void) closesocket(s);
\r
9475 (void) closesocket(s2);
\r
9480 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9481 err = WSAGetLastError();
\r
9482 (void) closesocket(s);
\r
9483 (void) closesocket(s2);
\r
9487 if (*user == NULLCHAR) user = UserName();
\r
9488 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9489 err = WSAGetLastError();
\r
9490 (void) closesocket(s);
\r
9491 (void) closesocket(s2);
\r
9495 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9496 err = WSAGetLastError();
\r
9497 (void) closesocket(s);
\r
9498 (void) closesocket(s2);
\r
9503 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9504 err = WSAGetLastError();
\r
9505 (void) closesocket(s);
\r
9506 (void) closesocket(s2);
\r
9510 (void) closesocket(s2); /* Stop listening */
\r
9512 /* Prepare return value */
\r
9513 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9514 cp->kind = CPRcmd;
\r
9517 *pr = (ProcRef *) cp;
\r
9524 AddInputSource(ProcRef pr, int lineByLine,
\r
9525 InputCallback func, VOIDSTAR closure)
\r
9527 InputSource *is, *is2 = NULL;
\r
9528 ChildProc *cp = (ChildProc *) pr;
\r
9530 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9531 is->lineByLine = lineByLine;
\r
9533 is->closure = closure;
\r
9534 is->second = NULL;
\r
9535 is->next = is->buf;
\r
9536 if (pr == NoProc) {
\r
9537 is->kind = CPReal;
\r
9538 consoleInputSource = is;
\r
9540 is->kind = cp->kind;
\r
9542 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9543 we create all threads suspended so that the is->hThread variable can be
\r
9544 safely assigned, then let the threads start with ResumeThread.
\r
9546 switch (cp->kind) {
\r
9548 is->hFile = cp->hFrom;
\r
9549 cp->hFrom = NULL; /* now owned by InputThread */
\r
9551 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9552 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9556 is->hFile = cp->hFrom;
\r
9557 cp->hFrom = NULL; /* now owned by InputThread */
\r
9559 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9560 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9564 is->sock = cp->sock;
\r
9566 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9567 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9571 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9573 is->sock = cp->sock;
\r
9575 is2->sock = cp->sock2;
\r
9576 is2->second = is2;
\r
9578 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9579 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9581 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9582 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9586 if( is->hThread != NULL ) {
\r
9587 ResumeThread( is->hThread );
\r
9590 if( is2 != NULL && is2->hThread != NULL ) {
\r
9591 ResumeThread( is2->hThread );
\r
9595 return (InputSourceRef) is;
\r
9599 RemoveInputSource(InputSourceRef isr)
\r
9603 is = (InputSource *) isr;
\r
9604 is->hThread = NULL; /* tell thread to stop */
\r
9605 CloseHandle(is->hThread);
\r
9606 if (is->second != NULL) {
\r
9607 is->second->hThread = NULL;
\r
9608 CloseHandle(is->second->hThread);
\r
9612 int no_wrap(char *message, int count)
\r
9614 ConsoleOutput(message, count, FALSE);
\r
9619 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9622 int outCount = SOCKET_ERROR;
\r
9623 ChildProc *cp = (ChildProc *) pr;
\r
9624 static OVERLAPPED ovl;
\r
9625 static int line = 0;
\r
9629 if (appData.noJoin || !appData.useInternalWrap)
\r
9630 return no_wrap(message, count);
\r
9633 int width = get_term_width();
\r
9634 int len = wrap(NULL, message, count, width, &line);
\r
9635 char *msg = malloc(len);
\r
9639 return no_wrap(message, count);
\r
9642 dbgchk = wrap(msg, message, count, width, &line);
\r
9643 if (dbgchk != len && appData.debugMode)
\r
9644 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9645 ConsoleOutput(msg, len, FALSE);
\r
9652 if (ovl.hEvent == NULL) {
\r
9653 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9655 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9657 switch (cp->kind) {
\r
9660 outCount = send(cp->sock, message, count, 0);
\r
9661 if (outCount == SOCKET_ERROR) {
\r
9662 *outError = WSAGetLastError();
\r
9664 *outError = NO_ERROR;
\r
9669 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9670 &dOutCount, NULL)) {
\r
9671 *outError = NO_ERROR;
\r
9672 outCount = (int) dOutCount;
\r
9674 *outError = GetLastError();
\r
9679 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9680 &dOutCount, &ovl);
\r
9681 if (*outError == NO_ERROR) {
\r
9682 outCount = (int) dOutCount;
\r
9692 if(n != 0) Sleep(n);
\r
9696 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9699 /* Ignore delay, not implemented for WinBoard */
\r
9700 return OutputToProcess(pr, message, count, outError);
\r
9705 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9706 char *buf, int count, int error)
\r
9708 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9711 /* see wgamelist.c for Game List functions */
\r
9712 /* see wedittags.c for Edit Tags functions */
\r
9719 char buf[MSG_SIZ];
\r
9722 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9723 f = fopen(buf, "r");
\r
9725 ProcessICSInitScript(f);
\r
9733 StartAnalysisClock()
\r
9735 if (analysisTimerEvent) return;
\r
9736 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9737 (UINT) 2000, NULL);
\r
9741 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9743 highlightInfo.sq[0].x = fromX;
\r
9744 highlightInfo.sq[0].y = fromY;
\r
9745 highlightInfo.sq[1].x = toX;
\r
9746 highlightInfo.sq[1].y = toY;
\r
9752 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9753 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9757 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9759 premoveHighlightInfo.sq[0].x = fromX;
\r
9760 premoveHighlightInfo.sq[0].y = fromY;
\r
9761 premoveHighlightInfo.sq[1].x = toX;
\r
9762 premoveHighlightInfo.sq[1].y = toY;
\r
9766 ClearPremoveHighlights()
\r
9768 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9769 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9773 ShutDownFrontEnd()
\r
9775 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9776 DeleteClipboardTempFiles();
\r
9782 if (IsIconic(hwndMain))
\r
9783 ShowWindow(hwndMain, SW_RESTORE);
\r
9785 SetActiveWindow(hwndMain);
\r
9789 * Prototypes for animation support routines
\r
9791 static void ScreenSquare(int column, int row, POINT * pt);
\r
9792 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9793 POINT frames[], int * nFrames);
\r
9799 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9800 { // [HGM] atomic: animate blast wave
\r
9803 explodeInfo.fromX = fromX;
\r
9804 explodeInfo.fromY = fromY;
\r
9805 explodeInfo.toX = toX;
\r
9806 explodeInfo.toY = toY;
\r
9807 for(i=1; i<4*kFactor; i++) {
\r
9808 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9809 DrawPosition(FALSE, board);
\r
9810 Sleep(appData.animSpeed);
\r
9812 explodeInfo.radius = 0;
\r
9813 DrawPosition(TRUE, board);
\r
9817 AnimateMove(board, fromX, fromY, toX, toY)
\r
9824 ChessSquare piece;
\r
9825 POINT start, finish, mid;
\r
9826 POINT frames[kFactor * 2 + 1];
\r
9829 if (!appData.animate) return;
\r
9830 if (doingSizing) return;
\r
9831 if (fromY < 0 || fromX < 0) return;
\r
9832 piece = board[fromY][fromX];
\r
9833 if (piece >= EmptySquare) return;
\r
9835 ScreenSquare(fromX, fromY, &start);
\r
9836 ScreenSquare(toX, toY, &finish);
\r
9838 /* All moves except knight jumps move in straight line */
\r
9839 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9840 mid.x = start.x + (finish.x - start.x) / 2;
\r
9841 mid.y = start.y + (finish.y - start.y) / 2;
\r
9843 /* Knight: make straight movement then diagonal */
\r
9844 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9845 mid.x = start.x + (finish.x - start.x) / 2;
\r
9849 mid.y = start.y + (finish.y - start.y) / 2;
\r
9853 /* Don't use as many frames for very short moves */
\r
9854 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9855 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9857 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9859 animInfo.from.x = fromX;
\r
9860 animInfo.from.y = fromY;
\r
9861 animInfo.to.x = toX;
\r
9862 animInfo.to.y = toY;
\r
9863 animInfo.lastpos = start;
\r
9864 animInfo.piece = piece;
\r
9865 for (n = 0; n < nFrames; n++) {
\r
9866 animInfo.pos = frames[n];
\r
9867 DrawPosition(FALSE, NULL);
\r
9868 animInfo.lastpos = animInfo.pos;
\r
9869 Sleep(appData.animSpeed);
\r
9871 animInfo.pos = finish;
\r
9872 DrawPosition(FALSE, NULL);
\r
9873 animInfo.piece = EmptySquare;
\r
9874 Explode(board, fromX, fromY, toX, toY);
\r
9877 /* Convert board position to corner of screen rect and color */
\r
9880 ScreenSquare(column, row, pt)
\r
9881 int column; int row; POINT * pt;
\r
9884 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9885 pt->y = lineGap + row * (squareSize + lineGap);
\r
9887 pt->x = lineGap + column * (squareSize + lineGap);
\r
9888 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9892 /* Generate a series of frame coords from start->mid->finish.
\r
9893 The movement rate doubles until the half way point is
\r
9894 reached, then halves back down to the final destination,
\r
9895 which gives a nice slow in/out effect. The algorithmn
\r
9896 may seem to generate too many intermediates for short
\r
9897 moves, but remember that the purpose is to attract the
\r
9898 viewers attention to the piece about to be moved and
\r
9899 then to where it ends up. Too few frames would be less
\r
9903 Tween(start, mid, finish, factor, frames, nFrames)
\r
9904 POINT * start; POINT * mid;
\r
9905 POINT * finish; int factor;
\r
9906 POINT frames[]; int * nFrames;
\r
9908 int n, fraction = 1, count = 0;
\r
9910 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9911 for (n = 0; n < factor; n++)
\r
9913 for (n = 0; n < factor; n++) {
\r
9914 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9915 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9917 fraction = fraction / 2;
\r
9921 frames[count] = *mid;
\r
9924 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9926 for (n = 0; n < factor; n++) {
\r
9927 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9928 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9930 fraction = fraction * 2;
\r
9936 SettingsPopUp(ChessProgramState *cps)
\r
9937 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9938 EngineOptionsPopup(savedHwnd, cps);
\r
9941 int flock(int fid, int code)
\r
9943 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9947 ov.OffsetHigh = 0;
\r
9949 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9950 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9951 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9952 default: return -1;
\r