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