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[][41 ] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
257 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
259 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
262 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
265 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
266 { ABOUTBOX2, IDC_ChessBoard },
\r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
268 OPT_GameListClose, IDC_GameListDoFilter },
\r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
270 { DLG_Error, IDOK },
\r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
272 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
275 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
276 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
277 { DLG_IndexNumber, IDC_Index },
\r
278 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
279 { DLG_TypeInName, IDOK, IDCANCEL },
\r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
281 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
283 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
284 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
285 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
286 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
287 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
288 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
290 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
291 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
292 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
293 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
294 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
295 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
296 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
297 GPB_General, GPB_Alarm },
\r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
299 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
300 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
301 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
302 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
303 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
304 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
305 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, 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
501 int cliWidth, cliHeight;
\r
504 SizeInfo sizeInfo[] =
\r
506 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
507 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
508 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
509 { "petite", 33, 1, 1, 1, 0, 0 },
\r
510 { "slim", 37, 2, 1, 0, 0, 0 },
\r
511 { "small", 40, 2, 1, 0, 0, 0 },
\r
512 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
513 { "middling", 49, 2, 0, 0, 0, 0 },
\r
514 { "average", 54, 2, 0, 0, 0, 0 },
\r
515 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
516 { "medium", 64, 3, 0, 0, 0, 0 },
\r
517 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
518 { "large", 80, 3, 0, 0, 0, 0 },
\r
519 { "big", 87, 3, 0, 0, 0, 0 },
\r
520 { "huge", 95, 3, 0, 0, 0, 0 },
\r
521 { "giant", 108, 3, 0, 0, 0, 0 },
\r
522 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
523 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
524 { NULL, 0, 0, 0, 0, 0, 0 }
\r
527 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
528 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
530 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
531 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
532 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
533 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
534 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
535 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
536 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
537 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
538 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
539 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
540 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
541 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
542 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
543 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
544 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
545 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
546 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
547 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
550 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
559 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
560 #define N_BUTTONS 5
\r
562 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
564 {"<<", IDM_ToStart, NULL, NULL},
\r
565 {"<", IDM_Backward, NULL, NULL},
\r
566 {"P", IDM_Pause, NULL, NULL},
\r
567 {">", IDM_Forward, NULL, NULL},
\r
568 {">>", IDM_ToEnd, NULL, NULL},
\r
571 int tinyLayout = 0, smallLayout = 0;
\r
572 #define MENU_BAR_ITEMS 9
\r
573 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
574 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
575 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
579 MySound sounds[(int)NSoundClasses];
\r
580 MyTextAttribs textAttribs[(int)NColorClasses];
\r
582 MyColorizeAttribs colorizeAttribs[] = {
\r
583 { (COLORREF)0, 0, N_("Shout Text") },
\r
584 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
585 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
586 { (COLORREF)0, 0, N_("Channel Text") },
\r
587 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
588 { (COLORREF)0, 0, N_("Tell Text") },
\r
589 { (COLORREF)0, 0, N_("Challenge Text") },
\r
590 { (COLORREF)0, 0, N_("Request Text") },
\r
591 { (COLORREF)0, 0, N_("Seek Text") },
\r
592 { (COLORREF)0, 0, N_("Normal Text") },
\r
593 { (COLORREF)0, 0, N_("None") }
\r
598 static char *commentTitle;
\r
599 static char *commentText;
\r
600 static int commentIndex;
\r
601 static Boolean editComment = FALSE;
\r
604 char errorTitle[MSG_SIZ];
\r
605 char errorMessage[2*MSG_SIZ];
\r
606 HWND errorDialog = NULL;
\r
607 BOOLEAN moveErrorMessageUp = FALSE;
\r
608 BOOLEAN consoleEcho = TRUE;
\r
609 CHARFORMAT consoleCF;
\r
610 COLORREF consoleBackgroundColor;
\r
612 char *programVersion;
\r
618 typedef int CPKind;
\r
627 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
630 #define INPUT_SOURCE_BUF_SIZE 4096
\r
632 typedef struct _InputSource {
\r
639 char buf[INPUT_SOURCE_BUF_SIZE];
\r
643 InputCallback func;
\r
644 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
648 InputSource *consoleInputSource;
\r
653 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
654 VOID ConsoleCreate();
\r
656 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
657 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
658 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
659 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
661 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
662 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
663 void ParseIcsTextMenu(char *icsTextMenuString);
\r
664 VOID PopUpNameDialog(char firstchar);
\r
665 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
669 int GameListOptions();
\r
671 int dummy; // [HGM] for obsolete args
\r
673 HWND hwndMain = NULL; /* root window*/
\r
674 HWND hwndConsole = NULL;
\r
675 HWND commentDialog = NULL;
\r
676 HWND moveHistoryDialog = NULL;
\r
677 HWND evalGraphDialog = NULL;
\r
678 HWND engineOutputDialog = NULL;
\r
679 HWND gameListDialog = NULL;
\r
680 HWND editTagsDialog = NULL;
\r
682 int commentUp = FALSE;
\r
684 WindowPlacement wpMain;
\r
685 WindowPlacement wpConsole;
\r
686 WindowPlacement wpComment;
\r
687 WindowPlacement wpMoveHistory;
\r
688 WindowPlacement wpEvalGraph;
\r
689 WindowPlacement wpEngineOutput;
\r
690 WindowPlacement wpGameList;
\r
691 WindowPlacement wpTags;
\r
693 VOID EngineOptionsPopup(); // [HGM] settings
\r
695 VOID GothicPopUp(char *title, VariantClass variant);
\r
697 * Setting "frozen" should disable all user input other than deleting
\r
698 * the window. We do this while engines are initializing themselves.
\r
700 static int frozen = 0;
\r
701 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
707 if (frozen) return;
\r
709 hmenu = GetMenu(hwndMain);
\r
710 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
711 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
713 DrawMenuBar(hwndMain);
\r
716 /* Undo a FreezeUI */
\r
722 if (!frozen) return;
\r
724 hmenu = GetMenu(hwndMain);
\r
725 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
726 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
728 DrawMenuBar(hwndMain);
\r
731 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
733 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
739 #define JAWS_ALT_INTERCEPT
\r
740 #define JAWS_KBUP_NAVIGATION
\r
741 #define JAWS_KBDOWN_NAVIGATION
\r
742 #define JAWS_MENU_ITEMS
\r
743 #define JAWS_SILENCE
\r
744 #define JAWS_REPLAY
\r
746 #define JAWS_COPYRIGHT
\r
747 #define JAWS_DELETE(X) X
\r
748 #define SAYMACHINEMOVE()
\r
752 /*---------------------------------------------------------------------------*\
\r
756 \*---------------------------------------------------------------------------*/
\r
759 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
760 LPSTR lpCmdLine, int nCmdShow)
\r
763 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
764 // INITCOMMONCONTROLSEX ex;
\r
768 LoadLibrary("RICHED32.DLL");
\r
769 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
771 if (!InitApplication(hInstance)) {
\r
774 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
781 // InitCommonControlsEx(&ex);
\r
782 InitCommonControls();
\r
784 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
785 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
786 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
788 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
790 while (GetMessage(&msg, /* message structure */
\r
791 NULL, /* handle of window receiving the message */
\r
792 0, /* lowest message to examine */
\r
793 0)) /* highest message to examine */
\r
796 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
797 // [HGM] navigate: switch between all windows with tab
\r
798 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
799 int i, currentElement = 0;
\r
801 // first determine what element of the chain we come from (if any)
\r
802 if(appData.icsActive) {
\r
803 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
804 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
806 if(engineOutputDialog && EngineOutputIsUp()) {
\r
807 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
808 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
810 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
811 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
813 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
814 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
815 if(msg.hwnd == e1) currentElement = 2; else
\r
816 if(msg.hwnd == e2) currentElement = 3; else
\r
817 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
818 if(msg.hwnd == mh) currentElement = 4; else
\r
819 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
820 if(msg.hwnd == hText) currentElement = 5; else
\r
821 if(msg.hwnd == hInput) currentElement = 6; else
\r
822 for (i = 0; i < N_BUTTONS; i++) {
\r
823 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
826 // determine where to go to
\r
827 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
829 currentElement = (currentElement + direction) % 7;
\r
830 switch(currentElement) {
\r
832 h = hwndMain; break; // passing this case always makes the loop exit
\r
834 h = buttonDesc[0].hwnd; break; // could be NULL
\r
836 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
839 if(!EngineOutputIsUp()) continue;
\r
842 if(!MoveHistoryIsUp()) continue;
\r
844 // case 6: // input to eval graph does not seem to get here!
\r
845 // if(!EvalGraphIsUp()) continue;
\r
846 // h = evalGraphDialog; break;
\r
848 if(!appData.icsActive) continue;
\r
852 if(!appData.icsActive) continue;
\r
858 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
859 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
862 continue; // this message now has been processed
\r
866 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
867 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
868 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
869 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
870 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
871 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
872 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
873 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
874 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
875 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
876 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
877 for(i=0; i<MAX_CHAT; i++)
\r
878 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
881 if(done) continue; // [HGM] chat: end patch
\r
882 TranslateMessage(&msg); /* Translates virtual key codes */
\r
883 DispatchMessage(&msg); /* Dispatches message to window */
\r
888 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
891 /*---------------------------------------------------------------------------*\
\r
893 * Initialization functions
\r
895 \*---------------------------------------------------------------------------*/
\r
899 { // update user logo if necessary
\r
900 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
902 if(appData.autoLogo) {
\r
903 curName = UserName();
\r
904 if(strcmp(curName, oldUserName)) {
\r
905 GetCurrentDirectory(MSG_SIZ, dir);
\r
906 SetCurrentDirectory(installDir);
\r
907 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
908 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
909 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
910 if(userLogo == NULL)
\r
911 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
912 SetCurrentDirectory(dir); /* return to prev directory */
\r
918 InitApplication(HINSTANCE hInstance)
\r
922 /* Fill in window class structure with parameters that describe the */
\r
925 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
926 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
927 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
928 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
929 wc.hInstance = hInstance; /* Owner of this class */
\r
930 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
931 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
932 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
933 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
934 wc.lpszClassName = szAppName; /* Name to register as */
\r
936 /* Register the window class and return success/failure code. */
\r
937 if (!RegisterClass(&wc)) return FALSE;
\r
939 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
940 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
942 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
943 wc.hInstance = hInstance;
\r
944 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
945 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
946 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
947 wc.lpszMenuName = NULL;
\r
948 wc.lpszClassName = szConsoleName;
\r
950 if (!RegisterClass(&wc)) return FALSE;
\r
955 /* Set by InitInstance, used by EnsureOnScreen */
\r
956 int screenHeight, screenWidth;
\r
959 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
961 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
962 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
963 if (*x > screenWidth - 32) *x = 0;
\r
964 if (*y > screenHeight - 32) *y = 0;
\r
965 if (*x < minX) *x = minX;
\r
966 if (*y < minY) *y = minY;
\r
970 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
972 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
973 GetCurrentDirectory(MSG_SIZ, dir);
\r
974 SetCurrentDirectory(installDir);
\r
975 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
976 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
978 if (cps->programLogo == NULL && appData.debugMode) {
\r
979 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
981 } else if(appData.autoLogo) {
\r
982 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
983 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
984 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
986 if(appData.directory[n] && appData.directory[n][0]) {
\r
987 SetCurrentDirectory(appData.directory[n]);
\r
988 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
991 SetCurrentDirectory(dir); /* return to prev directory */
\r
997 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
998 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1000 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1001 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1002 liteBackTextureMode = appData.liteBackTextureMode;
\r
1004 if (liteBackTexture == NULL && appData.debugMode) {
\r
1005 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1009 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1010 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1011 darkBackTextureMode = appData.darkBackTextureMode;
\r
1013 if (darkBackTexture == NULL && appData.debugMode) {
\r
1014 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1020 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1022 HWND hwnd; /* Main window handle. */
\r
1024 WINDOWPLACEMENT wp;
\r
1027 hInst = hInstance; /* Store instance handle in our global variable */
\r
1028 programName = szAppName;
\r
1030 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1031 *filepart = NULLCHAR;
\r
1033 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1035 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1036 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1037 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1038 /* xboard, and older WinBoards, controlled the move sound with the
\r
1039 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1040 always turn the option on (so that the backend will call us),
\r
1041 then let the user turn the sound off by setting it to silence if
\r
1042 desired. To accommodate old winboard.ini files saved by old
\r
1043 versions of WinBoard, we also turn off the sound if the option
\r
1044 was initially set to false. [HGM] taken out of InitAppData */
\r
1045 if (!appData.ringBellAfterMoves) {
\r
1046 sounds[(int)SoundMove].name = strdup("");
\r
1047 appData.ringBellAfterMoves = TRUE;
\r
1049 if (appData.debugMode) {
\r
1050 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1051 setbuf(debugFP, NULL);
\r
1054 LoadLanguageFile(appData.language);
\r
1058 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1059 // InitEngineUCI( installDir, &second );
\r
1061 /* Create a main window for this application instance. */
\r
1062 hwnd = CreateWindow(szAppName, szTitle,
\r
1063 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1064 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1065 NULL, NULL, hInstance, NULL);
\r
1068 /* If window could not be created, return "failure" */
\r
1073 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1074 LoadLogo(&first, 0, FALSE);
\r
1075 LoadLogo(&second, 1, appData.icsActive);
\r
1079 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1080 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1081 iconCurrent = iconWhite;
\r
1082 InitDrawingColors();
\r
1083 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1084 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1085 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1086 /* Compute window size for each board size, and use the largest
\r
1087 size that fits on this screen as the default. */
\r
1088 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1089 if (boardSize == (BoardSize)-1 &&
\r
1090 winH <= screenHeight
\r
1091 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1092 && winW <= screenWidth) {
\r
1093 boardSize = (BoardSize)ibs;
\r
1097 InitDrawingSizes(boardSize, 0);
\r
1099 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1101 /* [AS] Load textures if specified */
\r
1104 mysrandom( (unsigned) time(NULL) );
\r
1106 /* [AS] Restore layout */
\r
1107 if( wpMoveHistory.visible ) {
\r
1108 MoveHistoryPopUp();
\r
1111 if( wpEvalGraph.visible ) {
\r
1115 if( wpEngineOutput.visible ) {
\r
1116 EngineOutputPopUp();
\r
1119 /* Make the window visible; update its client area; and return "success" */
\r
1120 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1121 wp.length = sizeof(WINDOWPLACEMENT);
\r
1123 wp.showCmd = nCmdShow;
\r
1124 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1125 wp.rcNormalPosition.left = wpMain.x;
\r
1126 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1127 wp.rcNormalPosition.top = wpMain.y;
\r
1128 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1129 SetWindowPlacement(hwndMain, &wp);
\r
1131 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1133 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1134 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1136 if (hwndConsole) {
\r
1138 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1139 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1141 ShowWindow(hwndConsole, nCmdShow);
\r
1142 SetActiveWindow(hwndConsole);
\r
1144 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1145 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1154 HMENU hmenu = GetMenu(hwndMain);
\r
1156 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1157 MF_BYCOMMAND|((appData.icsActive &&
\r
1158 *appData.icsCommPort != NULLCHAR) ?
\r
1159 MF_ENABLED : MF_GRAYED));
\r
1160 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1161 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1162 MF_CHECKED : MF_UNCHECKED));
\r
1165 //---------------------------------------------------------------------------------------------------------
\r
1167 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1168 #define XBOARD FALSE
\r
1170 #define OPTCHAR "/"
\r
1171 #define SEPCHAR "="
\r
1175 // front-end part of option handling
\r
1178 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1180 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1181 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1184 lf->lfEscapement = 0;
\r
1185 lf->lfOrientation = 0;
\r
1186 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1187 lf->lfItalic = mfp->italic;
\r
1188 lf->lfUnderline = mfp->underline;
\r
1189 lf->lfStrikeOut = mfp->strikeout;
\r
1190 lf->lfCharSet = mfp->charset;
\r
1191 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1192 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1193 lf->lfQuality = DEFAULT_QUALITY;
\r
1194 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1195 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1199 CreateFontInMF(MyFont *mf)
\r
1201 LFfromMFP(&mf->lf, &mf->mfp);
\r
1202 if (mf->hf) DeleteObject(mf->hf);
\r
1203 mf->hf = CreateFontIndirect(&mf->lf);
\r
1206 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1208 colorVariable[] = {
\r
1209 &whitePieceColor,
\r
1210 &blackPieceColor,
\r
1211 &lightSquareColor,
\r
1212 &darkSquareColor,
\r
1213 &highlightSquareColor,
\r
1214 &premoveHighlightColor,
\r
1216 &consoleBackgroundColor,
\r
1217 &appData.fontForeColorWhite,
\r
1218 &appData.fontBackColorWhite,
\r
1219 &appData.fontForeColorBlack,
\r
1220 &appData.fontBackColorBlack,
\r
1221 &appData.evalHistColorWhite,
\r
1222 &appData.evalHistColorBlack,
\r
1223 &appData.highlightArrowColor,
\r
1226 /* Command line font name parser. NULL name means do nothing.
\r
1227 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1228 For backward compatibility, syntax without the colon is also
\r
1229 accepted, but font names with digits in them won't work in that case.
\r
1232 ParseFontName(char *name, MyFontParams *mfp)
\r
1235 if (name == NULL) return;
\r
1237 q = strchr(p, ':');
\r
1239 if (q - p >= sizeof(mfp->faceName))
\r
1240 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1241 memcpy(mfp->faceName, p, q - p);
\r
1242 mfp->faceName[q - p] = NULLCHAR;
\r
1245 q = mfp->faceName;
\r
1246 while (*p && !isdigit(*p)) {
\r
1248 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1249 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1251 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1254 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1255 mfp->pointSize = (float) atof(p);
\r
1256 mfp->bold = (strchr(p, 'b') != NULL);
\r
1257 mfp->italic = (strchr(p, 'i') != NULL);
\r
1258 mfp->underline = (strchr(p, 'u') != NULL);
\r
1259 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1260 mfp->charset = DEFAULT_CHARSET;
\r
1261 q = strchr(p, 'c');
\r
1263 mfp->charset = (BYTE) atoi(q+1);
\r
1267 ParseFont(char *name, int number)
\r
1268 { // wrapper to shield back-end from 'font'
\r
1269 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1274 { // in WB we have a 2D array of fonts; this initializes their description
\r
1276 /* Point font array elements to structures and
\r
1277 parse default font names */
\r
1278 for (i=0; i<NUM_FONTS; i++) {
\r
1279 for (j=0; j<NUM_SIZES; j++) {
\r
1280 font[j][i] = &fontRec[j][i];
\r
1281 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1288 { // here we create the actual fonts from the selected descriptions
\r
1290 for (i=0; i<NUM_FONTS; i++) {
\r
1291 for (j=0; j<NUM_SIZES; j++) {
\r
1292 CreateFontInMF(font[j][i]);
\r
1296 /* Color name parser.
\r
1297 X version accepts X color names, but this one
\r
1298 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1300 ParseColorName(char *name)
\r
1302 int red, green, blue, count;
\r
1303 char buf[MSG_SIZ];
\r
1305 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1307 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1308 &red, &green, &blue);
\r
1311 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1312 DisplayError(buf, 0);
\r
1313 return RGB(0, 0, 0);
\r
1315 return PALETTERGB(red, green, blue);
\r
1319 ParseColor(int n, char *name)
\r
1320 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1321 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1325 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1327 char *e = argValue;
\r
1331 if (*e == 'b') eff |= CFE_BOLD;
\r
1332 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1333 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1334 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1335 else if (*e == '#' || isdigit(*e)) break;
\r
1339 *color = ParseColorName(e);
\r
1343 ParseTextAttribs(ColorClass cc, char *s)
\r
1344 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1345 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1346 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1350 ParseBoardSize(void *addr, char *name)
\r
1351 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1352 BoardSize bs = SizeTiny;
\r
1353 while (sizeInfo[bs].name != NULL) {
\r
1354 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1355 *(BoardSize *)addr = bs;
\r
1360 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1365 { // [HGM] import name from appData first
\r
1368 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1369 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1370 textAttribs[cc].sound.data = NULL;
\r
1371 MyLoadSound(&textAttribs[cc].sound);
\r
1373 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1374 textAttribs[cc].sound.name = strdup("");
\r
1375 textAttribs[cc].sound.data = NULL;
\r
1377 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1378 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1379 sounds[sc].data = NULL;
\r
1380 MyLoadSound(&sounds[sc]);
\r
1385 SetCommPortDefaults()
\r
1387 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1388 dcb.DCBlength = sizeof(DCB);
\r
1389 dcb.BaudRate = 9600;
\r
1390 dcb.fBinary = TRUE;
\r
1391 dcb.fParity = FALSE;
\r
1392 dcb.fOutxCtsFlow = FALSE;
\r
1393 dcb.fOutxDsrFlow = FALSE;
\r
1394 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1395 dcb.fDsrSensitivity = FALSE;
\r
1396 dcb.fTXContinueOnXoff = TRUE;
\r
1397 dcb.fOutX = FALSE;
\r
1399 dcb.fNull = FALSE;
\r
1400 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1401 dcb.fAbortOnError = FALSE;
\r
1403 dcb.Parity = SPACEPARITY;
\r
1404 dcb.StopBits = ONESTOPBIT;
\r
1407 // [HGM] args: these three cases taken out to stay in front-end
\r
1409 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1410 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1411 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1412 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1414 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1415 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1416 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1417 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1418 ad->argName, mfp->faceName, mfp->pointSize,
\r
1419 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1420 mfp->bold ? "b" : "",
\r
1421 mfp->italic ? "i" : "",
\r
1422 mfp->underline ? "u" : "",
\r
1423 mfp->strikeout ? "s" : "",
\r
1424 (int)mfp->charset);
\r
1430 { // [HGM] copy the names from the internal WB variables to appData
\r
1433 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1434 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1435 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1436 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1440 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1441 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1442 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1443 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1444 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1445 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1446 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1447 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1448 (ta->effects) ? " " : "",
\r
1449 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1453 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1454 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1455 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1456 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1457 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1461 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1462 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1463 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1467 ParseCommPortSettings(char *s)
\r
1468 { // wrapper to keep dcb from back-end
\r
1469 ParseCommSettings(s, &dcb);
\r
1474 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1475 GetActualPlacement(hwndMain, &wpMain);
\r
1476 GetActualPlacement(hwndConsole, &wpConsole);
\r
1477 GetActualPlacement(commentDialog, &wpComment);
\r
1478 GetActualPlacement(editTagsDialog, &wpTags);
\r
1479 GetActualPlacement(gameListDialog, &wpGameList);
\r
1480 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1481 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1482 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1486 PrintCommPortSettings(FILE *f, char *name)
\r
1487 { // wrapper to shield back-end from DCB
\r
1488 PrintCommSettings(f, name, &dcb);
\r
1492 MySearchPath(char *installDir, char *name, char *fullname)
\r
1494 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1495 if(name[0]== '%') {
\r
1496 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1497 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1498 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1499 *strchr(buf, '%') = 0;
\r
1500 strcat(fullname, getenv(buf));
\r
1501 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1503 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1504 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1505 return (int) strlen(fullname);
\r
1507 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1511 MyGetFullPathName(char *name, char *fullname)
\r
1514 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1519 { // [HGM] args: allows testing if main window is realized from back-end
\r
1520 return hwndMain != NULL;
\r
1524 PopUpStartupDialog()
\r
1528 LoadLanguageFile(appData.language);
\r
1529 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1530 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1531 FreeProcInstance(lpProc);
\r
1534 /*---------------------------------------------------------------------------*\
\r
1536 * GDI board drawing routines
\r
1538 \*---------------------------------------------------------------------------*/
\r
1540 /* [AS] Draw square using background texture */
\r
1541 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1546 return; /* Should never happen! */
\r
1549 SetGraphicsMode( dst, GM_ADVANCED );
\r
1556 /* X reflection */
\r
1561 x.eDx = (FLOAT) dw + dx - 1;
\r
1564 SetWorldTransform( dst, &x );
\r
1567 /* Y reflection */
\r
1573 x.eDy = (FLOAT) dh + dy - 1;
\r
1575 SetWorldTransform( dst, &x );
\r
1583 x.eDx = (FLOAT) dx;
\r
1584 x.eDy = (FLOAT) dy;
\r
1587 SetWorldTransform( dst, &x );
\r
1591 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1599 SetWorldTransform( dst, &x );
\r
1601 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1604 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1606 PM_WP = (int) WhitePawn,
\r
1607 PM_WN = (int) WhiteKnight,
\r
1608 PM_WB = (int) WhiteBishop,
\r
1609 PM_WR = (int) WhiteRook,
\r
1610 PM_WQ = (int) WhiteQueen,
\r
1611 PM_WF = (int) WhiteFerz,
\r
1612 PM_WW = (int) WhiteWazir,
\r
1613 PM_WE = (int) WhiteAlfil,
\r
1614 PM_WM = (int) WhiteMan,
\r
1615 PM_WO = (int) WhiteCannon,
\r
1616 PM_WU = (int) WhiteUnicorn,
\r
1617 PM_WH = (int) WhiteNightrider,
\r
1618 PM_WA = (int) WhiteAngel,
\r
1619 PM_WC = (int) WhiteMarshall,
\r
1620 PM_WAB = (int) WhiteCardinal,
\r
1621 PM_WD = (int) WhiteDragon,
\r
1622 PM_WL = (int) WhiteLance,
\r
1623 PM_WS = (int) WhiteCobra,
\r
1624 PM_WV = (int) WhiteFalcon,
\r
1625 PM_WSG = (int) WhiteSilver,
\r
1626 PM_WG = (int) WhiteGrasshopper,
\r
1627 PM_WK = (int) WhiteKing,
\r
1628 PM_BP = (int) BlackPawn,
\r
1629 PM_BN = (int) BlackKnight,
\r
1630 PM_BB = (int) BlackBishop,
\r
1631 PM_BR = (int) BlackRook,
\r
1632 PM_BQ = (int) BlackQueen,
\r
1633 PM_BF = (int) BlackFerz,
\r
1634 PM_BW = (int) BlackWazir,
\r
1635 PM_BE = (int) BlackAlfil,
\r
1636 PM_BM = (int) BlackMan,
\r
1637 PM_BO = (int) BlackCannon,
\r
1638 PM_BU = (int) BlackUnicorn,
\r
1639 PM_BH = (int) BlackNightrider,
\r
1640 PM_BA = (int) BlackAngel,
\r
1641 PM_BC = (int) BlackMarshall,
\r
1642 PM_BG = (int) BlackGrasshopper,
\r
1643 PM_BAB = (int) BlackCardinal,
\r
1644 PM_BD = (int) BlackDragon,
\r
1645 PM_BL = (int) BlackLance,
\r
1646 PM_BS = (int) BlackCobra,
\r
1647 PM_BV = (int) BlackFalcon,
\r
1648 PM_BSG = (int) BlackSilver,
\r
1649 PM_BK = (int) BlackKing
\r
1652 static HFONT hPieceFont = NULL;
\r
1653 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1654 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1655 static int fontBitmapSquareSize = 0;
\r
1656 static char pieceToFontChar[(int) EmptySquare] =
\r
1657 { 'p', 'n', 'b', 'r', 'q',
\r
1658 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1659 'k', 'o', 'm', 'v', 't', 'w',
\r
1660 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1663 extern BOOL SetCharTable( char *table, const char * map );
\r
1664 /* [HGM] moved to backend.c */
\r
1666 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1669 BYTE r1 = GetRValue( color );
\r
1670 BYTE g1 = GetGValue( color );
\r
1671 BYTE b1 = GetBValue( color );
\r
1677 /* Create a uniform background first */
\r
1678 hbrush = CreateSolidBrush( color );
\r
1679 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1680 FillRect( hdc, &rc, hbrush );
\r
1681 DeleteObject( hbrush );
\r
1684 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1685 int steps = squareSize / 2;
\r
1688 for( i=0; i<steps; i++ ) {
\r
1689 BYTE r = r1 - (r1-r2) * i / steps;
\r
1690 BYTE g = g1 - (g1-g2) * i / steps;
\r
1691 BYTE b = b1 - (b1-b2) * i / steps;
\r
1693 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1694 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1695 FillRect( hdc, &rc, hbrush );
\r
1696 DeleteObject(hbrush);
\r
1699 else if( mode == 2 ) {
\r
1700 /* Diagonal gradient, good more or less for every piece */
\r
1701 POINT triangle[3];
\r
1702 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1703 HBRUSH hbrush_old;
\r
1704 int steps = squareSize;
\r
1707 triangle[0].x = squareSize - steps;
\r
1708 triangle[0].y = squareSize;
\r
1709 triangle[1].x = squareSize;
\r
1710 triangle[1].y = squareSize;
\r
1711 triangle[2].x = squareSize;
\r
1712 triangle[2].y = squareSize - steps;
\r
1714 for( i=0; i<steps; i++ ) {
\r
1715 BYTE r = r1 - (r1-r2) * i / steps;
\r
1716 BYTE g = g1 - (g1-g2) * i / steps;
\r
1717 BYTE b = b1 - (b1-b2) * i / steps;
\r
1719 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1720 hbrush_old = SelectObject( hdc, hbrush );
\r
1721 Polygon( hdc, triangle, 3 );
\r
1722 SelectObject( hdc, hbrush_old );
\r
1723 DeleteObject(hbrush);
\r
1728 SelectObject( hdc, hpen );
\r
1733 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1734 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1735 piece: follow the steps as explained below.
\r
1737 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1741 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1745 int backColor = whitePieceColor;
\r
1746 int foreColor = blackPieceColor;
\r
1748 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1749 backColor = appData.fontBackColorWhite;
\r
1750 foreColor = appData.fontForeColorWhite;
\r
1752 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1753 backColor = appData.fontBackColorBlack;
\r
1754 foreColor = appData.fontForeColorBlack;
\r
1758 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1760 hbm_old = SelectObject( hdc, hbm );
\r
1764 rc.right = squareSize;
\r
1765 rc.bottom = squareSize;
\r
1767 /* Step 1: background is now black */
\r
1768 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1770 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1772 pt.x = (squareSize - sz.cx) / 2;
\r
1773 pt.y = (squareSize - sz.cy) / 2;
\r
1775 SetBkMode( hdc, TRANSPARENT );
\r
1776 SetTextColor( hdc, chroma );
\r
1777 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1778 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1780 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1781 /* Step 3: the area outside the piece is filled with white */
\r
1782 // FloodFill( hdc, 0, 0, chroma );
\r
1783 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1784 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1785 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1786 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1787 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1789 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1790 but if the start point is not inside the piece we're lost!
\r
1791 There should be a better way to do this... if we could create a region or path
\r
1792 from the fill operation we would be fine for example.
\r
1794 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1795 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1797 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1798 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1799 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1801 SelectObject( dc2, bm2 );
\r
1802 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1803 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1804 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1805 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1806 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1809 DeleteObject( bm2 );
\r
1812 SetTextColor( hdc, 0 );
\r
1814 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1815 draw the piece again in black for safety.
\r
1817 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1819 SelectObject( hdc, hbm_old );
\r
1821 if( hPieceMask[index] != NULL ) {
\r
1822 DeleteObject( hPieceMask[index] );
\r
1825 hPieceMask[index] = hbm;
\r
1828 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1830 SelectObject( hdc, hbm );
\r
1833 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1834 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1835 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1837 SelectObject( dc1, hPieceMask[index] );
\r
1838 SelectObject( dc2, bm2 );
\r
1839 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1840 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1843 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1844 the piece background and deletes (makes transparent) the rest.
\r
1845 Thanks to that mask, we are free to paint the background with the greates
\r
1846 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1847 We use this, to make gradients and give the pieces a "roundish" look.
\r
1849 SetPieceBackground( hdc, backColor, 2 );
\r
1850 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1854 DeleteObject( bm2 );
\r
1857 SetTextColor( hdc, foreColor );
\r
1858 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1860 SelectObject( hdc, hbm_old );
\r
1862 if( hPieceFace[index] != NULL ) {
\r
1863 DeleteObject( hPieceFace[index] );
\r
1866 hPieceFace[index] = hbm;
\r
1869 static int TranslatePieceToFontPiece( int piece )
\r
1899 case BlackMarshall:
\r
1903 case BlackNightrider:
\r
1909 case BlackUnicorn:
\r
1913 case BlackGrasshopper:
\r
1925 case BlackCardinal:
\r
1932 case WhiteMarshall:
\r
1936 case WhiteNightrider:
\r
1942 case WhiteUnicorn:
\r
1946 case WhiteGrasshopper:
\r
1958 case WhiteCardinal:
\r
1967 void CreatePiecesFromFont()
\r
1970 HDC hdc_window = NULL;
\r
1976 if( fontBitmapSquareSize < 0 ) {
\r
1977 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1981 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1982 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1983 fontBitmapSquareSize = -1;
\r
1987 if( fontBitmapSquareSize != squareSize ) {
\r
1988 hdc_window = GetDC( hwndMain );
\r
1989 hdc = CreateCompatibleDC( hdc_window );
\r
1991 if( hPieceFont != NULL ) {
\r
1992 DeleteObject( hPieceFont );
\r
1995 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1996 hPieceMask[i] = NULL;
\r
1997 hPieceFace[i] = NULL;
\r
2003 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2004 fontHeight = appData.fontPieceSize;
\r
2007 fontHeight = (fontHeight * squareSize) / 100;
\r
2009 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2011 lf.lfEscapement = 0;
\r
2012 lf.lfOrientation = 0;
\r
2013 lf.lfWeight = FW_NORMAL;
\r
2015 lf.lfUnderline = 0;
\r
2016 lf.lfStrikeOut = 0;
\r
2017 lf.lfCharSet = DEFAULT_CHARSET;
\r
2018 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2019 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2020 lf.lfQuality = PROOF_QUALITY;
\r
2021 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2022 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2023 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2025 hPieceFont = CreateFontIndirect( &lf );
\r
2027 if( hPieceFont == NULL ) {
\r
2028 fontBitmapSquareSize = -2;
\r
2031 /* Setup font-to-piece character table */
\r
2032 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2033 /* No (or wrong) global settings, try to detect the font */
\r
2034 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2036 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2038 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2039 /* DiagramTT* family */
\r
2040 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2042 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2043 /* Fairy symbols */
\r
2044 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2046 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2047 /* Good Companion (Some characters get warped as literal :-( */
\r
2048 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2049 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2050 SetCharTable(pieceToFontChar, s);
\r
2053 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2054 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2058 /* Create bitmaps */
\r
2059 hfont_old = SelectObject( hdc, hPieceFont );
\r
2060 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2061 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2062 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2064 SelectObject( hdc, hfont_old );
\r
2066 fontBitmapSquareSize = squareSize;
\r
2070 if( hdc != NULL ) {
\r
2074 if( hdc_window != NULL ) {
\r
2075 ReleaseDC( hwndMain, hdc_window );
\r
2080 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2084 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2085 if (gameInfo.event &&
\r
2086 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2087 strcmp(name, "k80s") == 0) {
\r
2088 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2090 return LoadBitmap(hinst, name);
\r
2094 /* Insert a color into the program's logical palette
\r
2095 structure. This code assumes the given color is
\r
2096 the result of the RGB or PALETTERGB macro, and it
\r
2097 knows how those macros work (which is documented).
\r
2100 InsertInPalette(COLORREF color)
\r
2102 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2104 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2105 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2106 pLogPal->palNumEntries--;
\r
2110 pe->peFlags = (char) 0;
\r
2111 pe->peRed = (char) (0xFF & color);
\r
2112 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2113 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2119 InitDrawingColors()
\r
2121 if (pLogPal == NULL) {
\r
2122 /* Allocate enough memory for a logical palette with
\r
2123 * PALETTESIZE entries and set the size and version fields
\r
2124 * of the logical palette structure.
\r
2126 pLogPal = (NPLOGPALETTE)
\r
2127 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2128 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2129 pLogPal->palVersion = 0x300;
\r
2131 pLogPal->palNumEntries = 0;
\r
2133 InsertInPalette(lightSquareColor);
\r
2134 InsertInPalette(darkSquareColor);
\r
2135 InsertInPalette(whitePieceColor);
\r
2136 InsertInPalette(blackPieceColor);
\r
2137 InsertInPalette(highlightSquareColor);
\r
2138 InsertInPalette(premoveHighlightColor);
\r
2140 /* create a logical color palette according the information
\r
2141 * in the LOGPALETTE structure.
\r
2143 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2145 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2146 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2147 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2148 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2149 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2150 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2151 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2152 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2153 /* [AS] Force rendering of the font-based pieces */
\r
2154 if( fontBitmapSquareSize > 0 ) {
\r
2155 fontBitmapSquareSize = 0;
\r
2161 BoardWidth(int boardSize, int n)
\r
2162 { /* [HGM] argument n added to allow different width and height */
\r
2163 int lineGap = sizeInfo[boardSize].lineGap;
\r
2165 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2166 lineGap = appData.overrideLineGap;
\r
2169 return (n + 1) * lineGap +
\r
2170 n * sizeInfo[boardSize].squareSize;
\r
2173 /* Respond to board resize by dragging edge */
\r
2175 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2177 BoardSize newSize = NUM_SIZES - 1;
\r
2178 static int recurse = 0;
\r
2179 if (IsIconic(hwndMain)) return;
\r
2180 if (recurse > 0) return;
\r
2182 while (newSize > 0) {
\r
2183 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2184 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2185 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2188 boardSize = newSize;
\r
2189 InitDrawingSizes(boardSize, flags);
\r
2194 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2197 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2199 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2200 ChessSquare piece;
\r
2201 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2203 SIZE clockSize, messageSize;
\r
2205 char buf[MSG_SIZ];
\r
2207 HMENU hmenu = GetMenu(hwndMain);
\r
2208 RECT crect, wrect, oldRect;
\r
2210 LOGBRUSH logbrush;
\r
2212 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2213 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2215 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2216 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2218 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2219 oldRect.top = wpMain.y;
\r
2220 oldRect.right = wpMain.x + wpMain.width;
\r
2221 oldRect.bottom = wpMain.y + wpMain.height;
\r
2223 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2224 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2225 squareSize = sizeInfo[boardSize].squareSize;
\r
2226 lineGap = sizeInfo[boardSize].lineGap;
\r
2227 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2229 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2230 lineGap = appData.overrideLineGap;
\r
2233 if (tinyLayout != oldTinyLayout) {
\r
2234 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2236 style &= ~WS_SYSMENU;
\r
2237 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2238 "&Minimize\tCtrl+F4");
\r
2240 style |= WS_SYSMENU;
\r
2241 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2243 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2245 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2246 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2247 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2249 DrawMenuBar(hwndMain);
\r
2252 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2253 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2255 /* Get text area sizes */
\r
2256 hdc = GetDC(hwndMain);
\r
2257 if (appData.clockMode) {
\r
2258 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2260 snprintf(buf, MSG_SIZ, _("White"));
\r
2262 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2263 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2264 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2265 str = _("We only care about the height here");
\r
2266 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2267 SelectObject(hdc, oldFont);
\r
2268 ReleaseDC(hwndMain, hdc);
\r
2270 /* Compute where everything goes */
\r
2271 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2272 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2273 logoHeight = 2*clockSize.cy;
\r
2274 leftLogoRect.left = OUTER_MARGIN;
\r
2275 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2276 leftLogoRect.top = OUTER_MARGIN;
\r
2277 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2279 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2280 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2281 rightLogoRect.top = OUTER_MARGIN;
\r
2282 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2285 whiteRect.left = leftLogoRect.right;
\r
2286 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2287 whiteRect.top = OUTER_MARGIN;
\r
2288 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2290 blackRect.right = rightLogoRect.left;
\r
2291 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2292 blackRect.top = whiteRect.top;
\r
2293 blackRect.bottom = whiteRect.bottom;
\r
2295 whiteRect.left = OUTER_MARGIN;
\r
2296 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2297 whiteRect.top = OUTER_MARGIN;
\r
2298 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2300 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2301 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2302 blackRect.top = whiteRect.top;
\r
2303 blackRect.bottom = whiteRect.bottom;
\r
2305 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2308 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2309 if (appData.showButtonBar) {
\r
2310 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2311 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2313 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2315 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2316 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2318 boardRect.left = OUTER_MARGIN;
\r
2319 boardRect.right = boardRect.left + boardWidth;
\r
2320 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2321 boardRect.bottom = boardRect.top + boardHeight;
\r
2323 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2324 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2325 oldBoardSize = boardSize;
\r
2326 oldTinyLayout = tinyLayout;
\r
2327 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2328 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2329 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2330 winW *= 1 + twoBoards;
\r
2331 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2332 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2333 wpMain.height = winH; // without disturbing window attachments
\r
2334 GetWindowRect(hwndMain, &wrect);
\r
2335 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2336 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2338 // [HGM] placement: let attached windows follow size change.
\r
2339 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2340 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2341 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2345 /* compensate if menu bar wrapped */
\r
2346 GetClientRect(hwndMain, &crect);
\r
2347 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2348 wpMain.height += offby;
\r
2350 case WMSZ_TOPLEFT:
\r
2351 SetWindowPos(hwndMain, NULL,
\r
2352 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2353 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2356 case WMSZ_TOPRIGHT:
\r
2358 SetWindowPos(hwndMain, NULL,
\r
2359 wrect.left, wrect.bottom - wpMain.height,
\r
2360 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2363 case WMSZ_BOTTOMLEFT:
\r
2365 SetWindowPos(hwndMain, NULL,
\r
2366 wrect.right - wpMain.width, wrect.top,
\r
2367 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2370 case WMSZ_BOTTOMRIGHT:
\r
2374 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2375 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2380 for (i = 0; i < N_BUTTONS; i++) {
\r
2381 if (buttonDesc[i].hwnd != NULL) {
\r
2382 DestroyWindow(buttonDesc[i].hwnd);
\r
2383 buttonDesc[i].hwnd = NULL;
\r
2385 if (appData.showButtonBar) {
\r
2386 buttonDesc[i].hwnd =
\r
2387 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2388 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2389 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2390 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2391 (HMENU) buttonDesc[i].id,
\r
2392 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2394 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2395 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2396 MAKELPARAM(FALSE, 0));
\r
2398 if (buttonDesc[i].id == IDM_Pause)
\r
2399 hwndPause = buttonDesc[i].hwnd;
\r
2400 buttonDesc[i].wndproc = (WNDPROC)
\r
2401 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2404 if (gridPen != NULL) DeleteObject(gridPen);
\r
2405 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2406 if (premovePen != NULL) DeleteObject(premovePen);
\r
2407 if (lineGap != 0) {
\r
2408 logbrush.lbStyle = BS_SOLID;
\r
2409 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2411 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2412 lineGap, &logbrush, 0, NULL);
\r
2413 logbrush.lbColor = highlightSquareColor;
\r
2415 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2416 lineGap, &logbrush, 0, NULL);
\r
2418 logbrush.lbColor = premoveHighlightColor;
\r
2420 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2421 lineGap, &logbrush, 0, NULL);
\r
2423 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2424 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2425 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2426 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2427 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2428 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2429 BOARD_WIDTH * (squareSize + lineGap);
\r
2430 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2432 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2433 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2434 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2435 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2436 lineGap / 2 + (i * (squareSize + lineGap));
\r
2437 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2438 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2439 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2443 /* [HGM] Licensing requirement */
\r
2445 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2448 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2450 GothicPopUp( "", VariantNormal);
\r
2453 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2455 /* Load piece bitmaps for this board size */
\r
2456 for (i=0; i<=2; i++) {
\r
2457 for (piece = WhitePawn;
\r
2458 (int) piece < (int) BlackPawn;
\r
2459 piece = (ChessSquare) ((int) piece + 1)) {
\r
2460 if (pieceBitmap[i][piece] != NULL)
\r
2461 DeleteObject(pieceBitmap[i][piece]);
\r
2465 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2466 // Orthodox Chess pieces
\r
2467 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2468 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2469 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2470 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2472 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2473 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2474 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2475 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2477 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2478 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2479 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2480 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2482 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2483 // in Shogi, Hijack the unused Queen for Lance
\r
2484 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2488 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2489 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2490 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2493 if(squareSize <= 72 && squareSize >= 33) {
\r
2494 /* A & C are available in most sizes now */
\r
2495 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2496 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2499 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2502 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2508 } else { // Smirf-like
\r
2509 if(gameInfo.variant == VariantSChess) {
\r
2510 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2519 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2520 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2523 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2524 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2527 } else { // WinBoard standard
\r
2528 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2535 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2536 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2567 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2568 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2595 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2596 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2597 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2598 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2599 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2600 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2601 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2604 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2605 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2606 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2607 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2608 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2609 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2613 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2614 /* special Shogi support in this size */
\r
2615 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2616 for (piece = WhitePawn;
\r
2617 (int) piece < (int) BlackPawn;
\r
2618 piece = (ChessSquare) ((int) piece + 1)) {
\r
2619 if (pieceBitmap[i][piece] != NULL)
\r
2620 DeleteObject(pieceBitmap[i][piece]);
\r
2623 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2637 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2651 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2670 PieceBitmap(ChessSquare p, int kind)
\r
2672 if ((int) p >= (int) BlackPawn)
\r
2673 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2675 return pieceBitmap[kind][(int) p];
\r
2678 /***************************************************************/
\r
2680 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2681 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2683 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2684 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2688 SquareToPos(int row, int column, int * x, int * y)
\r
2691 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2692 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2694 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2695 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2700 DrawCoordsOnDC(HDC hdc)
\r
2702 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2703 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2704 char str[2] = { NULLCHAR, NULLCHAR };
\r
2705 int oldMode, oldAlign, x, y, start, i;
\r
2709 if (!appData.showCoords)
\r
2712 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2714 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2715 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2716 oldAlign = GetTextAlign(hdc);
\r
2717 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2719 y = boardRect.top + lineGap;
\r
2720 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2722 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2723 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2724 str[0] = files[start + i];
\r
2725 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2726 y += squareSize + lineGap;
\r
2729 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2731 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2732 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2733 str[0] = ranks[start + i];
\r
2734 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2735 x += squareSize + lineGap;
\r
2738 SelectObject(hdc, oldBrush);
\r
2739 SetBkMode(hdc, oldMode);
\r
2740 SetTextAlign(hdc, oldAlign);
\r
2741 SelectObject(hdc, oldFont);
\r
2745 DrawGridOnDC(HDC hdc)
\r
2749 if (lineGap != 0) {
\r
2750 oldPen = SelectObject(hdc, gridPen);
\r
2751 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2752 SelectObject(hdc, oldPen);
\r
2756 #define HIGHLIGHT_PEN 0
\r
2757 #define PREMOVE_PEN 1
\r
2760 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2763 HPEN oldPen, hPen;
\r
2764 if (lineGap == 0) return;
\r
2766 x1 = boardRect.left +
\r
2767 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2768 y1 = boardRect.top +
\r
2769 lineGap/2 + y * (squareSize + lineGap);
\r
2771 x1 = boardRect.left +
\r
2772 lineGap/2 + x * (squareSize + lineGap);
\r
2773 y1 = boardRect.top +
\r
2774 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2776 hPen = pen ? premovePen : highlightPen;
\r
2777 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2778 MoveToEx(hdc, x1, y1, NULL);
\r
2779 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2780 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2781 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2782 LineTo(hdc, x1, y1);
\r
2783 SelectObject(hdc, oldPen);
\r
2787 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2790 for (i=0; i<2; i++) {
\r
2791 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2792 DrawHighlightOnDC(hdc, TRUE,
\r
2793 h->sq[i].x, h->sq[i].y,
\r
2798 /* Note: sqcolor is used only in monoMode */
\r
2799 /* Note that this code is largely duplicated in woptions.c,
\r
2800 function DrawSampleSquare, so that needs to be updated too */
\r
2802 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2804 HBITMAP oldBitmap;
\r
2808 if (appData.blindfold) return;
\r
2810 /* [AS] Use font-based pieces if needed */
\r
2811 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2812 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2813 CreatePiecesFromFont();
\r
2815 if( fontBitmapSquareSize == squareSize ) {
\r
2816 int index = TranslatePieceToFontPiece(piece);
\r
2818 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2820 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2821 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2825 squareSize, squareSize,
\r
2830 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2832 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2833 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2837 squareSize, squareSize,
\r
2846 if (appData.monoMode) {
\r
2847 SelectObject(tmphdc, PieceBitmap(piece,
\r
2848 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2849 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2850 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2852 tmpSize = squareSize;
\r
2854 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2855 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2856 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2857 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2858 x += (squareSize - minorSize)>>1;
\r
2859 y += squareSize - minorSize - 2;
\r
2860 tmpSize = minorSize;
\r
2862 if (color || appData.allWhite ) {
\r
2863 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2865 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2866 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2867 if(appData.upsideDown && color==flipView)
\r
2868 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2870 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2871 /* Use black for outline of white pieces */
\r
2872 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2873 if(appData.upsideDown && color==flipView)
\r
2874 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2876 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2878 /* Use square color for details of black pieces */
\r
2879 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2880 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2881 if(appData.upsideDown && !flipView)
\r
2882 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2884 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2886 SelectObject(hdc, oldBrush);
\r
2887 SelectObject(tmphdc, oldBitmap);
\r
2891 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2892 int GetBackTextureMode( int algo )
\r
2894 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2898 case BACK_TEXTURE_MODE_PLAIN:
\r
2899 result = 1; /* Always use identity map */
\r
2901 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2902 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2910 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2911 to handle redraws cleanly (as random numbers would always be different).
\r
2913 VOID RebuildTextureSquareInfo()
\r
2923 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2925 if( liteBackTexture != NULL ) {
\r
2926 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2927 lite_w = bi.bmWidth;
\r
2928 lite_h = bi.bmHeight;
\r
2932 if( darkBackTexture != NULL ) {
\r
2933 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2934 dark_w = bi.bmWidth;
\r
2935 dark_h = bi.bmHeight;
\r
2939 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2940 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2941 if( (col + row) & 1 ) {
\r
2943 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2944 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2945 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2947 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2948 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2949 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2951 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2952 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2957 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2958 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2959 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2961 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2962 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2963 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2965 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2966 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2973 /* [AS] Arrow highlighting support */
\r
2975 static double A_WIDTH = 5; /* Width of arrow body */
\r
2977 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2978 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2980 static double Sqr( double x )
\r
2985 static int Round( double x )
\r
2987 return (int) (x + 0.5);
\r
2990 /* Draw an arrow between two points using current settings */
\r
2991 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2994 double dx, dy, j, k, x, y;
\r
2996 if( d_x == s_x ) {
\r
2997 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2999 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3002 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3003 arrow[1].y = d_y - h;
\r
3005 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3006 arrow[2].y = d_y - h;
\r
3011 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3012 arrow[5].y = d_y - h;
\r
3014 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3015 arrow[4].y = d_y - h;
\r
3017 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3020 else if( d_y == s_y ) {
\r
3021 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3024 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3026 arrow[1].x = d_x - w;
\r
3027 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3029 arrow[2].x = d_x - w;
\r
3030 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3035 arrow[5].x = d_x - w;
\r
3036 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3038 arrow[4].x = d_x - w;
\r
3039 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3042 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3045 /* [AS] Needed a lot of paper for this! :-) */
\r
3046 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3047 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3049 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3051 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3056 arrow[0].x = Round(x - j);
\r
3057 arrow[0].y = Round(y + j*dx);
\r
3059 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3060 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3063 x = (double) d_x - k;
\r
3064 y = (double) d_y - k*dy;
\r
3067 x = (double) d_x + k;
\r
3068 y = (double) d_y + k*dy;
\r
3071 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3073 arrow[6].x = Round(x - j);
\r
3074 arrow[6].y = Round(y + j*dx);
\r
3076 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3077 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3079 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3080 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3085 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3086 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3089 Polygon( hdc, arrow, 7 );
\r
3092 /* [AS] Draw an arrow between two squares */
\r
3093 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3095 int s_x, s_y, d_x, d_y;
\r
3102 if( s_col == d_col && s_row == d_row ) {
\r
3106 /* Get source and destination points */
\r
3107 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3108 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3111 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3113 else if( d_y < s_y ) {
\r
3114 d_y += squareSize / 2 + squareSize / 4;
\r
3117 d_y += squareSize / 2;
\r
3121 d_x += squareSize / 2 - squareSize / 4;
\r
3123 else if( d_x < s_x ) {
\r
3124 d_x += squareSize / 2 + squareSize / 4;
\r
3127 d_x += squareSize / 2;
\r
3130 s_x += squareSize / 2;
\r
3131 s_y += squareSize / 2;
\r
3133 /* Adjust width */
\r
3134 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3137 stLB.lbStyle = BS_SOLID;
\r
3138 stLB.lbColor = appData.highlightArrowColor;
\r
3141 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3142 holdpen = SelectObject( hdc, hpen );
\r
3143 hbrush = CreateBrushIndirect( &stLB );
\r
3144 holdbrush = SelectObject( hdc, hbrush );
\r
3146 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3148 SelectObject( hdc, holdpen );
\r
3149 SelectObject( hdc, holdbrush );
\r
3150 DeleteObject( hpen );
\r
3151 DeleteObject( hbrush );
\r
3154 BOOL HasHighlightInfo()
\r
3156 BOOL result = FALSE;
\r
3158 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3159 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3167 BOOL IsDrawArrowEnabled()
\r
3169 BOOL result = FALSE;
\r
3171 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3178 VOID DrawArrowHighlight( HDC hdc )
\r
3180 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3181 DrawArrowBetweenSquares( hdc,
\r
3182 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3183 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3187 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3189 HRGN result = NULL;
\r
3191 if( HasHighlightInfo() ) {
\r
3192 int x1, y1, x2, y2;
\r
3193 int sx, sy, dx, dy;
\r
3195 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3196 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3198 sx = MIN( x1, x2 );
\r
3199 sy = MIN( y1, y2 );
\r
3200 dx = MAX( x1, x2 ) + squareSize;
\r
3201 dy = MAX( y1, y2 ) + squareSize;
\r
3203 result = CreateRectRgn( sx, sy, dx, dy );
\r
3210 Warning: this function modifies the behavior of several other functions.
\r
3212 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3213 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3214 repaint is scattered all over the place, which is not good for features such as
\r
3215 "arrow highlighting" that require a full repaint of the board.
\r
3217 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3218 user interaction, when speed is not so important) but especially to avoid errors
\r
3219 in the displayed graphics.
\r
3221 In such patched places, I always try refer to this function so there is a single
\r
3222 place to maintain knowledge.
\r
3224 To restore the original behavior, just return FALSE unconditionally.
\r
3226 BOOL IsFullRepaintPreferrable()
\r
3228 BOOL result = FALSE;
\r
3230 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3231 /* Arrow may appear on the board */
\r
3239 This function is called by DrawPosition to know whether a full repaint must
\r
3242 Only DrawPosition may directly call this function, which makes use of
\r
3243 some state information. Other function should call DrawPosition specifying
\r
3244 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3246 BOOL DrawPositionNeedsFullRepaint()
\r
3248 BOOL result = FALSE;
\r
3251 Probably a slightly better policy would be to trigger a full repaint
\r
3252 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3253 but animation is fast enough that it's difficult to notice.
\r
3255 if( animInfo.piece == EmptySquare ) {
\r
3256 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3265 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3267 int row, column, x, y, square_color, piece_color;
\r
3268 ChessSquare piece;
\r
3270 HDC texture_hdc = NULL;
\r
3272 /* [AS] Initialize background textures if needed */
\r
3273 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3274 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3275 if( backTextureSquareSize != squareSize
\r
3276 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3277 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3278 backTextureSquareSize = squareSize;
\r
3279 RebuildTextureSquareInfo();
\r
3282 texture_hdc = CreateCompatibleDC( hdc );
\r
3285 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3286 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3288 SquareToPos(row, column, &x, &y);
\r
3290 piece = board[row][column];
\r
3292 square_color = ((column + row) % 2) == 1;
\r
3293 if( gameInfo.variant == VariantXiangqi ) {
\r
3294 square_color = !InPalace(row, column);
\r
3295 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3296 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3298 piece_color = (int) piece < (int) BlackPawn;
\r
3301 /* [HGM] holdings file: light square or black */
\r
3302 if(column == BOARD_LEFT-2) {
\r
3303 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3306 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3310 if(column == BOARD_RGHT + 1 ) {
\r
3311 if( row < gameInfo.holdingsSize )
\r
3314 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3318 if(column == BOARD_LEFT-1 ) /* left align */
\r
3319 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3320 else if( column == BOARD_RGHT) /* right align */
\r
3321 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3323 if (appData.monoMode) {
\r
3324 if (piece == EmptySquare) {
\r
3325 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3326 square_color ? WHITENESS : BLACKNESS);
\r
3328 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3331 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3332 /* [AS] Draw the square using a texture bitmap */
\r
3333 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3334 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3335 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3338 squareSize, squareSize,
\r
3341 backTextureSquareInfo[r][c].mode,
\r
3342 backTextureSquareInfo[r][c].x,
\r
3343 backTextureSquareInfo[r][c].y );
\r
3345 SelectObject( texture_hdc, hbm );
\r
3347 if (piece != EmptySquare) {
\r
3348 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3352 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3354 oldBrush = SelectObject(hdc, brush );
\r
3355 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3356 SelectObject(hdc, oldBrush);
\r
3357 if (piece != EmptySquare)
\r
3358 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3363 if( texture_hdc != NULL ) {
\r
3364 DeleteDC( texture_hdc );
\r
3368 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3369 void fputDW(FILE *f, int x)
\r
3371 fputc(x & 255, f);
\r
3372 fputc(x>>8 & 255, f);
\r
3373 fputc(x>>16 & 255, f);
\r
3374 fputc(x>>24 & 255, f);
\r
3377 #define MAX_CLIPS 200 /* more than enough */
\r
3380 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3382 // HBITMAP bufferBitmap;
\r
3387 int w = 100, h = 50;
\r
3389 if(logo == NULL) {
\r
3390 if(!logoHeight) return;
\r
3391 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3393 // GetClientRect(hwndMain, &Rect);
\r
3394 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3395 // Rect.bottom-Rect.top+1);
\r
3396 tmphdc = CreateCompatibleDC(hdc);
\r
3397 hbm = SelectObject(tmphdc, logo);
\r
3398 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3402 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3403 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3404 SelectObject(tmphdc, hbm);
\r
3412 HDC hdc = GetDC(hwndMain);
\r
3413 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3414 if(appData.autoLogo) {
\r
3416 switch(gameMode) { // pick logos based on game mode
\r
3417 case IcsObserving:
\r
3418 whiteLogo = second.programLogo; // ICS logo
\r
3419 blackLogo = second.programLogo;
\r
3422 case IcsPlayingWhite:
\r
3423 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3424 blackLogo = second.programLogo; // ICS logo
\r
3426 case IcsPlayingBlack:
\r
3427 whiteLogo = second.programLogo; // ICS logo
\r
3428 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3430 case TwoMachinesPlay:
\r
3431 if(first.twoMachinesColor[0] == 'b') {
\r
3432 whiteLogo = second.programLogo;
\r
3433 blackLogo = first.programLogo;
\r
3436 case MachinePlaysWhite:
\r
3437 blackLogo = userLogo;
\r
3439 case MachinePlaysBlack:
\r
3440 whiteLogo = userLogo;
\r
3441 blackLogo = first.programLogo;
\r
3444 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3445 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3446 ReleaseDC(hwndMain, hdc);
\r
3451 UpdateLogos(int display)
\r
3452 { // called after loading new engine(s), in tourney or from menu
\r
3453 LoadLogo(&first, 0, FALSE);
\r
3454 LoadLogo(&second, 1, appData.icsActive);
\r
3455 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3456 if(display) DisplayLogos();
\r
3459 static HDC hdcSeek;
\r
3461 // [HGM] seekgraph
\r
3462 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3465 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3466 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3467 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3468 SelectObject( hdcSeek, hp );
\r
3471 // front-end wrapper for drawing functions to do rectangles
\r
3472 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3477 if (hdcSeek == NULL) {
\r
3478 hdcSeek = GetDC(hwndMain);
\r
3479 if (!appData.monoMode) {
\r
3480 SelectPalette(hdcSeek, hPal, FALSE);
\r
3481 RealizePalette(hdcSeek);
\r
3484 hp = SelectObject( hdcSeek, gridPen );
\r
3485 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3486 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3487 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3488 SelectObject( hdcSeek, hp );
\r
3491 // front-end wrapper for putting text in graph
\r
3492 void DrawSeekText(char *buf, int x, int y)
\r
3495 SetBkMode( hdcSeek, TRANSPARENT );
\r
3496 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3497 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3500 void DrawSeekDot(int x, int y, int color)
\r
3502 int square = color & 0x80;
\r
3503 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3504 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3507 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3508 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3510 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3511 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3512 SelectObject(hdcSeek, oldBrush);
\r
3516 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3518 static Board lastReq[2], lastDrawn[2];
\r
3519 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3520 static int lastDrawnFlipView = 0;
\r
3521 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3522 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3525 HBITMAP bufferBitmap;
\r
3526 HBITMAP oldBitmap;
\r
3528 HRGN clips[MAX_CLIPS];
\r
3529 ChessSquare dragged_piece = EmptySquare;
\r
3530 int nr = twoBoards*partnerUp;
\r
3532 /* I'm undecided on this - this function figures out whether a full
\r
3533 * repaint is necessary on its own, so there's no real reason to have the
\r
3534 * caller tell it that. I think this can safely be set to FALSE - but
\r
3535 * if we trust the callers not to request full repaints unnessesarily, then
\r
3536 * we could skip some clipping work. In other words, only request a full
\r
3537 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3538 * gamestart and similar) --Hawk
\r
3540 Boolean fullrepaint = repaint;
\r
3542 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3544 if( DrawPositionNeedsFullRepaint() ) {
\r
3545 fullrepaint = TRUE;
\r
3548 if (board == NULL) {
\r
3549 if (!lastReqValid[nr]) {
\r
3552 board = lastReq[nr];
\r
3554 CopyBoard(lastReq[nr], board);
\r
3555 lastReqValid[nr] = 1;
\r
3558 if (doingSizing) {
\r
3562 if (IsIconic(hwndMain)) {
\r
3566 if (hdc == NULL) {
\r
3567 hdc = GetDC(hwndMain);
\r
3568 if (!appData.monoMode) {
\r
3569 SelectPalette(hdc, hPal, FALSE);
\r
3570 RealizePalette(hdc);
\r
3574 releaseDC = FALSE;
\r
3577 /* Create some work-DCs */
\r
3578 hdcmem = CreateCompatibleDC(hdc);
\r
3579 tmphdc = CreateCompatibleDC(hdc);
\r
3581 /* If dragging is in progress, we temporarely remove the piece */
\r
3582 /* [HGM] or temporarily decrease count if stacked */
\r
3583 /* !! Moved to before board compare !! */
\r
3584 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3585 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3586 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3587 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3588 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3590 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3591 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3592 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3594 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3597 /* Figure out which squares need updating by comparing the
\r
3598 * newest board with the last drawn board and checking if
\r
3599 * flipping has changed.
\r
3601 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3602 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3603 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3604 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3605 SquareToPos(row, column, &x, &y);
\r
3606 clips[num_clips++] =
\r
3607 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3611 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3612 for (i=0; i<2; i++) {
\r
3613 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3614 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3615 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3616 lastDrawnHighlight.sq[i].y >= 0) {
\r
3617 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3618 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3619 clips[num_clips++] =
\r
3620 CreateRectRgn(x - lineGap, y - lineGap,
\r
3621 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3623 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3624 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3625 clips[num_clips++] =
\r
3626 CreateRectRgn(x - lineGap, y - lineGap,
\r
3627 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3631 for (i=0; i<2; i++) {
\r
3632 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3633 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3634 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3635 lastDrawnPremove.sq[i].y >= 0) {
\r
3636 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3637 lastDrawnPremove.sq[i].x, &x, &y);
\r
3638 clips[num_clips++] =
\r
3639 CreateRectRgn(x - lineGap, y - lineGap,
\r
3640 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3642 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3643 premoveHighlightInfo.sq[i].y >= 0) {
\r
3644 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3645 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3646 clips[num_clips++] =
\r
3647 CreateRectRgn(x - lineGap, y - lineGap,
\r
3648 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3652 } else { // nr == 1
\r
3653 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3654 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3655 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3656 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3657 for (i=0; i<2; i++) {
\r
3658 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3659 partnerHighlightInfo.sq[i].y >= 0) {
\r
3660 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3661 partnerHighlightInfo.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 (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3667 oldPartnerHighlight.sq[i].y >= 0) {
\r
3668 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3669 oldPartnerHighlight.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
3677 fullrepaint = TRUE;
\r
3680 /* Create a buffer bitmap - this is the actual bitmap
\r
3681 * being written to. When all the work is done, we can
\r
3682 * copy it to the real DC (the screen). This avoids
\r
3683 * the problems with flickering.
\r
3685 GetClientRect(hwndMain, &Rect);
\r
3686 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3687 Rect.bottom-Rect.top+1);
\r
3688 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3689 if (!appData.monoMode) {
\r
3690 SelectPalette(hdcmem, hPal, FALSE);
\r
3693 /* Create clips for dragging */
\r
3694 if (!fullrepaint) {
\r
3695 if (dragInfo.from.x >= 0) {
\r
3696 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3697 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3699 if (dragInfo.start.x >= 0) {
\r
3700 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3701 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3703 if (dragInfo.pos.x >= 0) {
\r
3704 x = dragInfo.pos.x - squareSize / 2;
\r
3705 y = dragInfo.pos.y - squareSize / 2;
\r
3706 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3708 if (dragInfo.lastpos.x >= 0) {
\r
3709 x = dragInfo.lastpos.x - squareSize / 2;
\r
3710 y = dragInfo.lastpos.y - squareSize / 2;
\r
3711 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3715 /* Are we animating a move?
\r
3717 * - remove the piece from the board (temporarely)
\r
3718 * - calculate the clipping region
\r
3720 if (!fullrepaint) {
\r
3721 if (animInfo.piece != EmptySquare) {
\r
3722 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3723 x = boardRect.left + animInfo.lastpos.x;
\r
3724 y = boardRect.top + animInfo.lastpos.y;
\r
3725 x2 = boardRect.left + animInfo.pos.x;
\r
3726 y2 = boardRect.top + animInfo.pos.y;
\r
3727 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3728 /* Slight kludge. The real problem is that after AnimateMove is
\r
3729 done, the position on the screen does not match lastDrawn.
\r
3730 This currently causes trouble only on e.p. captures in
\r
3731 atomic, where the piece moves to an empty square and then
\r
3732 explodes. The old and new positions both had an empty square
\r
3733 at the destination, but animation has drawn a piece there and
\r
3734 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3735 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3739 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3740 if (num_clips == 0)
\r
3741 fullrepaint = TRUE;
\r
3743 /* Set clipping on the memory DC */
\r
3744 if (!fullrepaint) {
\r
3745 SelectClipRgn(hdcmem, clips[0]);
\r
3746 for (x = 1; x < num_clips; x++) {
\r
3747 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3748 abort(); // this should never ever happen!
\r
3752 /* Do all the drawing to the memory DC */
\r
3753 if(explodeInfo.radius) { // [HGM] atomic
\r
3755 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3756 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3757 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3758 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3759 x += squareSize/2;
\r
3760 y += squareSize/2;
\r
3761 if(!fullrepaint) {
\r
3762 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3763 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3765 DrawGridOnDC(hdcmem);
\r
3766 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3767 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3768 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3769 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3770 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3771 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3772 SelectObject(hdcmem, oldBrush);
\r
3774 DrawGridOnDC(hdcmem);
\r
3775 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3776 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3777 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3779 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3780 oldPartnerHighlight = partnerHighlightInfo;
\r
3782 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3784 if(nr == 0) // [HGM] dual: markers only on left board
\r
3785 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3786 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3787 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3788 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3789 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3790 SquareToPos(row, column, &x, &y);
\r
3791 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3792 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3793 SelectObject(hdcmem, oldBrush);
\r
3798 if( appData.highlightMoveWithArrow ) {
\r
3799 DrawArrowHighlight(hdcmem);
\r
3802 DrawCoordsOnDC(hdcmem);
\r
3804 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3805 /* to make sure lastDrawn contains what is actually drawn */
\r
3807 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3808 if (dragged_piece != EmptySquare) {
\r
3809 /* [HGM] or restack */
\r
3810 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3811 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3813 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3814 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3815 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3816 x = dragInfo.pos.x - squareSize / 2;
\r
3817 y = dragInfo.pos.y - squareSize / 2;
\r
3818 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3819 ((int) dragInfo.piece < (int) BlackPawn),
\r
3820 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3823 /* Put the animated piece back into place and draw it */
\r
3824 if (animInfo.piece != EmptySquare) {
\r
3825 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3826 x = boardRect.left + animInfo.pos.x;
\r
3827 y = boardRect.top + animInfo.pos.y;
\r
3828 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3829 ((int) animInfo.piece < (int) BlackPawn),
\r
3830 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3833 /* Release the bufferBitmap by selecting in the old bitmap
\r
3834 * and delete the memory DC
\r
3836 SelectObject(hdcmem, oldBitmap);
\r
3839 /* Set clipping on the target DC */
\r
3840 if (!fullrepaint) {
\r
3841 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3843 GetRgnBox(clips[x], &rect);
\r
3844 DeleteObject(clips[x]);
\r
3845 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3846 rect.right + wpMain.width/2, rect.bottom);
\r
3848 SelectClipRgn(hdc, clips[0]);
\r
3849 for (x = 1; x < num_clips; x++) {
\r
3850 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3851 abort(); // this should never ever happen!
\r
3855 /* Copy the new bitmap onto the screen in one go.
\r
3856 * This way we avoid any flickering
\r
3858 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3859 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3860 boardRect.right - boardRect.left,
\r
3861 boardRect.bottom - boardRect.top,
\r
3862 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3863 if(saveDiagFlag) {
\r
3864 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3865 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3867 GetObject(bufferBitmap, sizeof(b), &b);
\r
3868 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3869 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3870 bih.biWidth = b.bmWidth;
\r
3871 bih.biHeight = b.bmHeight;
\r
3873 bih.biBitCount = b.bmBitsPixel;
\r
3874 bih.biCompression = 0;
\r
3875 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3876 bih.biXPelsPerMeter = 0;
\r
3877 bih.biYPelsPerMeter = 0;
\r
3878 bih.biClrUsed = 0;
\r
3879 bih.biClrImportant = 0;
\r
3880 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3881 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3882 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3883 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3885 wb = b.bmWidthBytes;
\r
3887 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3888 int k = ((int*) pData)[i];
\r
3889 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3890 if(j >= 16) break;
\r
3892 if(j >= nrColors) nrColors = j+1;
\r
3894 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3896 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3897 for(w=0; w<(wb>>2); w+=2) {
\r
3898 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3899 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3900 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3901 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3902 pData[p++] = m | j<<4;
\r
3904 while(p&3) pData[p++] = 0;
\r
3907 wb = ((wb+31)>>5)<<2;
\r
3909 // write BITMAPFILEHEADER
\r
3910 fprintf(diagFile, "BM");
\r
3911 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3912 fputDW(diagFile, 0);
\r
3913 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3914 // write BITMAPINFOHEADER
\r
3915 fputDW(diagFile, 40);
\r
3916 fputDW(diagFile, b.bmWidth);
\r
3917 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3918 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3919 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3920 fputDW(diagFile, 0);
\r
3921 fputDW(diagFile, 0);
\r
3922 fputDW(diagFile, 0);
\r
3923 fputDW(diagFile, 0);
\r
3924 fputDW(diagFile, 0);
\r
3925 fputDW(diagFile, 0);
\r
3926 // write color table
\r
3928 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3929 // write bitmap data
\r
3930 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3931 fputc(pData[i], diagFile);
\r
3936 SelectObject(tmphdc, oldBitmap);
\r
3938 /* Massive cleanup */
\r
3939 for (x = 0; x < num_clips; x++)
\r
3940 DeleteObject(clips[x]);
\r
3943 DeleteObject(bufferBitmap);
\r
3946 ReleaseDC(hwndMain, hdc);
\r
3948 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3950 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3952 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3955 /* CopyBoard(lastDrawn, board);*/
\r
3956 lastDrawnHighlight = highlightInfo;
\r
3957 lastDrawnPremove = premoveHighlightInfo;
\r
3958 lastDrawnFlipView = flipView;
\r
3959 lastDrawnValid[nr] = 1;
\r
3962 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3967 saveDiagFlag = 1; diagFile = f;
\r
3968 HDCDrawPosition(NULL, TRUE, NULL);
\r
3976 /*---------------------------------------------------------------------------*\
\r
3977 | CLIENT PAINT PROCEDURE
\r
3978 | This is the main event-handler for the WM_PAINT message.
\r
3980 \*---------------------------------------------------------------------------*/
\r
3982 PaintProc(HWND hwnd)
\r
3988 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3989 if (IsIconic(hwnd)) {
\r
3990 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3992 if (!appData.monoMode) {
\r
3993 SelectPalette(hdc, hPal, FALSE);
\r
3994 RealizePalette(hdc);
\r
3996 HDCDrawPosition(hdc, 1, NULL);
\r
3997 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3998 flipView = !flipView; partnerUp = !partnerUp;
\r
3999 HDCDrawPosition(hdc, 1, NULL);
\r
4000 flipView = !flipView; partnerUp = !partnerUp;
\r
4003 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4004 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4005 ETO_CLIPPED|ETO_OPAQUE,
\r
4006 &messageRect, messageText, strlen(messageText), NULL);
\r
4007 SelectObject(hdc, oldFont);
\r
4008 DisplayBothClocks();
\r
4011 EndPaint(hwnd,&ps);
\r
4019 * If the user selects on a border boundary, return -1; if off the board,
\r
4020 * return -2. Otherwise map the event coordinate to the square.
\r
4021 * The offset boardRect.left or boardRect.top must already have been
\r
4022 * subtracted from x.
\r
4024 int EventToSquare(x, limit)
\r
4032 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4034 x /= (squareSize + lineGap);
\r
4046 DropEnable dropEnables[] = {
\r
4047 { 'P', DP_Pawn, N_("Pawn") },
\r
4048 { 'N', DP_Knight, N_("Knight") },
\r
4049 { 'B', DP_Bishop, N_("Bishop") },
\r
4050 { 'R', DP_Rook, N_("Rook") },
\r
4051 { 'Q', DP_Queen, N_("Queen") },
\r
4055 SetupDropMenu(HMENU hmenu)
\r
4057 int i, count, enable;
\r
4059 extern char white_holding[], black_holding[];
\r
4060 char item[MSG_SIZ];
\r
4062 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4063 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4064 dropEnables[i].piece);
\r
4066 while (p && *p++ == dropEnables[i].piece) count++;
\r
4067 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4068 enable = count > 0 || !appData.testLegality
\r
4069 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4070 && !appData.icsActive);
\r
4071 ModifyMenu(hmenu, dropEnables[i].command,
\r
4072 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4073 dropEnables[i].command, item);
\r
4077 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4079 dragInfo.lastpos.x = boardRect.left + x;
\r
4080 dragInfo.lastpos.y = boardRect.top + y;
\r
4081 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4082 dragInfo.from.x = fromX;
\r
4083 dragInfo.from.y = fromY;
\r
4084 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4085 dragInfo.start = dragInfo.from;
\r
4086 SetCapture(hwndMain);
\r
4089 void DragPieceEnd(int x, int y)
\r
4092 dragInfo.start.x = dragInfo.start.y = -1;
\r
4093 dragInfo.from = dragInfo.start;
\r
4094 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4097 void ChangeDragPiece(ChessSquare piece)
\r
4099 dragInfo.piece = piece;
\r
4102 /* Event handler for mouse messages */
\r
4104 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4108 static int recursive = 0;
\r
4110 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4113 if (message == WM_MBUTTONUP) {
\r
4114 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4115 to the middle button: we simulate pressing the left button too!
\r
4117 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4118 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4124 pt.x = LOWORD(lParam);
\r
4125 pt.y = HIWORD(lParam);
\r
4126 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4127 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4128 if (!flipView && y >= 0) {
\r
4129 y = BOARD_HEIGHT - 1 - y;
\r
4131 if (flipView && x >= 0) {
\r
4132 x = BOARD_WIDTH - 1 - x;
\r
4135 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4137 switch (message) {
\r
4138 case WM_LBUTTONDOWN:
\r
4139 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4140 ClockClick(flipClock); break;
\r
4141 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4142 ClockClick(!flipClock); break;
\r
4144 dragInfo.start.x = dragInfo.start.y = -1;
\r
4145 dragInfo.from = dragInfo.start;
\r
4146 if(fromX == -1 && frozen) { // not sure where this is for
\r
4147 fromX = fromY = -1;
\r
4148 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4151 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4152 DrawPosition(TRUE, NULL);
\r
4155 case WM_LBUTTONUP:
\r
4156 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4157 DrawPosition(TRUE, NULL);
\r
4160 case WM_MOUSEMOVE:
\r
4161 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4162 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4163 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4164 if ((appData.animateDragging || appData.highlightDragging)
\r
4165 && (wParam & MK_LBUTTON)
\r
4166 && dragInfo.from.x >= 0)
\r
4168 BOOL full_repaint = FALSE;
\r
4170 if (appData.animateDragging) {
\r
4171 dragInfo.pos = pt;
\r
4173 if (appData.highlightDragging) {
\r
4174 SetHighlights(fromX, fromY, x, y);
\r
4175 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4176 full_repaint = TRUE;
\r
4180 DrawPosition( full_repaint, NULL);
\r
4182 dragInfo.lastpos = dragInfo.pos;
\r
4186 case WM_MOUSEWHEEL: // [DM]
\r
4187 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4188 /* Mouse Wheel is being rolled forward
\r
4189 * Play moves forward
\r
4191 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4192 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4193 /* Mouse Wheel is being rolled backward
\r
4194 * Play moves backward
\r
4196 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4197 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4201 case WM_MBUTTONUP:
\r
4202 case WM_RBUTTONUP:
\r
4204 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4207 case WM_MBUTTONDOWN:
\r
4208 case WM_RBUTTONDOWN:
\r
4211 fromX = fromY = -1;
\r
4212 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4213 dragInfo.start.x = dragInfo.start.y = -1;
\r
4214 dragInfo.from = dragInfo.start;
\r
4215 dragInfo.lastpos = dragInfo.pos;
\r
4216 if (appData.highlightDragging) {
\r
4217 ClearHighlights();
\r
4220 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4221 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4222 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4223 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4224 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4228 DrawPosition(TRUE, NULL);
\r
4230 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4233 if (message == WM_MBUTTONDOWN) {
\r
4234 buttonCount = 3; /* even if system didn't think so */
\r
4235 if (wParam & MK_SHIFT)
\r
4236 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4238 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4239 } else { /* message == WM_RBUTTONDOWN */
\r
4240 /* Just have one menu, on the right button. Windows users don't
\r
4241 think to try the middle one, and sometimes other software steals
\r
4242 it, or it doesn't really exist. */
\r
4243 if(gameInfo.variant != VariantShogi)
\r
4244 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4246 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4250 SetCapture(hwndMain);
4253 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4254 SetupDropMenu(hmenu);
\r
4255 MenuPopup(hwnd, pt, hmenu, -1);
\r
4265 /* Preprocess messages for buttons in main window */
\r
4267 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4269 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4272 for (i=0; i<N_BUTTONS; i++) {
\r
4273 if (buttonDesc[i].id == id) break;
\r
4275 if (i == N_BUTTONS) return 0;
\r
4276 switch (message) {
\r
4281 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4282 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4289 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4292 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4293 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4294 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4295 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4297 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4299 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4300 TypeInEvent((char)wParam);
\r
4306 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4309 /* Process messages for Promotion dialog box */
\r
4311 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4315 switch (message) {
\r
4316 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4317 /* Center the dialog over the application window */
\r
4318 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4319 Translate(hDlg, DLG_PromotionKing);
\r
4320 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4321 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4322 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4323 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4324 SW_SHOW : SW_HIDE);
\r
4325 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4326 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4327 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4328 PieceToChar(WhiteAngel) != '~') ||
\r
4329 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4330 PieceToChar(BlackAngel) != '~') ) ?
\r
4331 SW_SHOW : SW_HIDE);
\r
4332 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4333 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4334 PieceToChar(WhiteMarshall) != '~') ||
\r
4335 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4336 PieceToChar(BlackMarshall) != '~') ) ?
\r
4337 SW_SHOW : SW_HIDE);
\r
4338 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4339 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4340 gameInfo.variant != VariantShogi ?
\r
4341 SW_SHOW : SW_HIDE);
\r
4342 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4343 gameInfo.variant != VariantShogi ?
\r
4344 SW_SHOW : SW_HIDE);
\r
4345 if(gameInfo.variant == VariantShogi) {
\r
4346 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4347 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4348 SetWindowText(hDlg, "Promote?");
\r
4350 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4351 gameInfo.variant == VariantSuper ?
\r
4352 SW_SHOW : SW_HIDE);
\r
4355 case WM_COMMAND: /* message: received a command */
\r
4356 switch (LOWORD(wParam)) {
\r
4358 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4359 ClearHighlights();
\r
4360 DrawPosition(FALSE, NULL);
\r
4363 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4366 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4369 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4370 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4373 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4374 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4376 case PB_Chancellor:
\r
4377 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4379 case PB_Archbishop:
\r
4380 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4383 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4388 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4389 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4390 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4391 fromX = fromY = -1;
\r
4392 if (!appData.highlightLastMove) {
\r
4393 ClearHighlights();
\r
4394 DrawPosition(FALSE, NULL);
\r
4401 /* Pop up promotion dialog */
\r
4403 PromotionPopup(HWND hwnd)
\r
4407 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4408 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4409 hwnd, (DLGPROC)lpProc);
\r
4410 FreeProcInstance(lpProc);
\r
4416 DrawPosition(TRUE, NULL);
\r
4417 PromotionPopup(hwndMain);
\r
4420 /* Toggle ShowThinking */
\r
4422 ToggleShowThinking()
\r
4424 appData.showThinking = !appData.showThinking;
\r
4425 ShowThinkingEvent();
\r
4429 LoadGameDialog(HWND hwnd, char* title)
\r
4433 char fileTitle[MSG_SIZ];
\r
4434 f = OpenFileDialog(hwnd, "rb", "",
\r
4435 appData.oldSaveStyle ? "gam" : "pgn",
\r
4437 title, &number, fileTitle, NULL);
\r
4439 cmailMsgLoaded = FALSE;
\r
4440 if (number == 0) {
\r
4441 int error = GameListBuild(f);
\r
4443 DisplayError(_("Cannot build game list"), error);
\r
4444 } else if (!ListEmpty(&gameList) &&
\r
4445 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4446 GameListPopUp(f, fileTitle);
\r
4449 GameListDestroy();
\r
4452 LoadGame(f, number, fileTitle, FALSE);
\r
4456 int get_term_width()
\r
4461 HFONT hfont, hold_font;
\r
4466 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4470 // get the text metrics
\r
4471 hdc = GetDC(hText);
\r
4472 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4473 if (consoleCF.dwEffects & CFE_BOLD)
\r
4474 lf.lfWeight = FW_BOLD;
\r
4475 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4476 lf.lfItalic = TRUE;
\r
4477 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4478 lf.lfStrikeOut = TRUE;
\r
4479 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4480 lf.lfUnderline = TRUE;
\r
4481 hfont = CreateFontIndirect(&lf);
\r
4482 hold_font = SelectObject(hdc, hfont);
\r
4483 GetTextMetrics(hdc, &tm);
\r
4484 SelectObject(hdc, hold_font);
\r
4485 DeleteObject(hfont);
\r
4486 ReleaseDC(hText, hdc);
\r
4488 // get the rectangle
\r
4489 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4491 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4494 void UpdateICSWidth(HWND hText)
\r
4496 LONG old_width, new_width;
\r
4498 new_width = get_term_width(hText, FALSE);
\r
4499 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4500 if (new_width != old_width)
\r
4502 ics_update_width(new_width);
\r
4503 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4508 ChangedConsoleFont()
\r
4511 CHARRANGE tmpsel, sel;
\r
4512 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4513 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4514 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4517 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4518 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4519 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4520 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4521 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4522 * size. This was undocumented in the version of MSVC++ that I had
\r
4523 * when I wrote the code, but is apparently documented now.
\r
4525 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4526 cfmt.bCharSet = f->lf.lfCharSet;
\r
4527 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4528 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4529 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4530 /* Why are the following seemingly needed too? */
\r
4531 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4532 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4533 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4535 tmpsel.cpMax = -1; /*999999?*/
\r
4536 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4537 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4538 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4539 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4541 paraf.cbSize = sizeof(paraf);
\r
4542 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4543 paraf.dxStartIndent = 0;
\r
4544 paraf.dxOffset = WRAP_INDENT;
\r
4545 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4546 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4547 UpdateICSWidth(hText);
\r
4550 /*---------------------------------------------------------------------------*\
\r
4552 * Window Proc for main window
\r
4554 \*---------------------------------------------------------------------------*/
\r
4556 /* Process messages for main window, etc. */
\r
4558 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4561 int wmId, wmEvent;
\r
4565 char fileTitle[MSG_SIZ];
\r
4566 char buf[MSG_SIZ];
\r
4567 static SnapData sd;
\r
4568 static int peek=0;
\r
4570 switch (message) {
\r
4572 case WM_PAINT: /* message: repaint portion of window */
\r
4576 case WM_ERASEBKGND:
\r
4577 if (IsIconic(hwnd)) {
\r
4578 /* Cheat; change the message */
\r
4579 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4581 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4585 case WM_LBUTTONDOWN:
\r
4586 case WM_MBUTTONDOWN:
\r
4587 case WM_RBUTTONDOWN:
\r
4588 case WM_LBUTTONUP:
\r
4589 case WM_MBUTTONUP:
\r
4590 case WM_RBUTTONUP:
\r
4591 case WM_MOUSEMOVE:
\r
4592 case WM_MOUSEWHEEL:
\r
4593 MouseEvent(hwnd, message, wParam, lParam);
\r
4597 if((char)wParam == '\b') {
\r
4598 ForwardEvent(); peek = 0;
\r
4601 JAWS_KBUP_NAVIGATION
\r
4606 if((char)wParam == '\b') {
\r
4607 if(!peek) BackwardEvent(), peek = 1;
\r
4610 JAWS_KBDOWN_NAVIGATION
\r
4616 JAWS_ALT_INTERCEPT
\r
4618 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4619 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4620 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4621 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4623 SendMessage(h, message, wParam, lParam);
\r
4624 } else if(lParam != KF_REPEAT) {
\r
4625 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4626 TypeInEvent((char)wParam);
\r
4627 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4628 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4633 case WM_PALETTECHANGED:
\r
4634 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4636 HDC hdc = GetDC(hwndMain);
\r
4637 SelectPalette(hdc, hPal, TRUE);
\r
4638 nnew = RealizePalette(hdc);
\r
4640 paletteChanged = TRUE;
\r
4641 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4643 ReleaseDC(hwnd, hdc);
\r
4647 case WM_QUERYNEWPALETTE:
\r
4648 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4650 HDC hdc = GetDC(hwndMain);
\r
4651 paletteChanged = FALSE;
\r
4652 SelectPalette(hdc, hPal, FALSE);
\r
4653 nnew = RealizePalette(hdc);
\r
4655 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4657 ReleaseDC(hwnd, hdc);
\r
4662 case WM_COMMAND: /* message: command from application menu */
\r
4663 wmId = LOWORD(wParam);
\r
4664 wmEvent = HIWORD(wParam);
\r
4669 SAY("new game enter a move to play against the computer with white");
\r
4672 case IDM_NewGameFRC:
\r
4673 if( NewGameFRC() == 0 ) {
\r
4678 case IDM_NewVariant:
\r
4679 NewVariantPopup(hwnd);
\r
4682 case IDM_LoadGame:
\r
4683 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4686 case IDM_LoadNextGame:
\r
4690 case IDM_LoadPrevGame:
\r
4694 case IDM_ReloadGame:
\r
4698 case IDM_LoadPosition:
\r
4699 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4700 Reset(FALSE, TRUE);
\r
4703 f = OpenFileDialog(hwnd, "rb", "",
\r
4704 appData.oldSaveStyle ? "pos" : "fen",
\r
4706 _("Load Position from File"), &number, fileTitle, NULL);
\r
4708 LoadPosition(f, number, fileTitle);
\r
4712 case IDM_LoadNextPosition:
\r
4713 ReloadPosition(1);
\r
4716 case IDM_LoadPrevPosition:
\r
4717 ReloadPosition(-1);
\r
4720 case IDM_ReloadPosition:
\r
4721 ReloadPosition(0);
\r
4724 case IDM_SaveGame:
\r
4725 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4726 f = OpenFileDialog(hwnd, "a", defName,
\r
4727 appData.oldSaveStyle ? "gam" : "pgn",
\r
4729 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4731 SaveGame(f, 0, "");
\r
4735 case IDM_SavePosition:
\r
4736 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4737 f = OpenFileDialog(hwnd, "a", defName,
\r
4738 appData.oldSaveStyle ? "pos" : "fen",
\r
4740 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4742 SavePosition(f, 0, "");
\r
4746 case IDM_SaveDiagram:
\r
4747 defName = "diagram";
\r
4748 f = OpenFileDialog(hwnd, "wb", defName,
\r
4751 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4757 case IDM_CopyGame:
\r
4758 CopyGameToClipboard();
\r
4761 case IDM_PasteGame:
\r
4762 PasteGameFromClipboard();
\r
4765 case IDM_CopyGameListToClipboard:
\r
4766 CopyGameListToClipboard();
\r
4769 /* [AS] Autodetect FEN or PGN data */
\r
4770 case IDM_PasteAny:
\r
4771 PasteGameOrFENFromClipboard();
\r
4774 /* [AS] Move history */
\r
4775 case IDM_ShowMoveHistory:
\r
4776 if( MoveHistoryIsUp() ) {
\r
4777 MoveHistoryPopDown();
\r
4780 MoveHistoryPopUp();
\r
4784 /* [AS] Eval graph */
\r
4785 case IDM_ShowEvalGraph:
\r
4786 if( EvalGraphIsUp() ) {
\r
4787 EvalGraphPopDown();
\r
4791 SetFocus(hwndMain);
\r
4795 /* [AS] Engine output */
\r
4796 case IDM_ShowEngineOutput:
\r
4797 if( EngineOutputIsUp() ) {
\r
4798 EngineOutputPopDown();
\r
4801 EngineOutputPopUp();
\r
4805 /* [AS] User adjudication */
\r
4806 case IDM_UserAdjudication_White:
\r
4807 UserAdjudicationEvent( +1 );
\r
4810 case IDM_UserAdjudication_Black:
\r
4811 UserAdjudicationEvent( -1 );
\r
4814 case IDM_UserAdjudication_Draw:
\r
4815 UserAdjudicationEvent( 0 );
\r
4818 /* [AS] Game list options dialog */
\r
4819 case IDM_GameListOptions:
\r
4820 GameListOptions();
\r
4827 case IDM_CopyPosition:
\r
4828 CopyFENToClipboard();
\r
4831 case IDM_PastePosition:
\r
4832 PasteFENFromClipboard();
\r
4835 case IDM_MailMove:
\r
4839 case IDM_ReloadCMailMsg:
\r
4840 Reset(TRUE, TRUE);
\r
4841 ReloadCmailMsgEvent(FALSE);
\r
4844 case IDM_Minimize:
\r
4845 ShowWindow(hwnd, SW_MINIMIZE);
\r
4852 case IDM_MachineWhite:
\r
4853 MachineWhiteEvent();
\r
4855 * refresh the tags dialog only if it's visible
\r
4857 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4859 tags = PGNTags(&gameInfo);
\r
4860 TagsPopUp(tags, CmailMsg());
\r
4863 SAY("computer starts playing white");
\r
4866 case IDM_MachineBlack:
\r
4867 MachineBlackEvent();
\r
4869 * refresh the tags dialog only if it's visible
\r
4871 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4873 tags = PGNTags(&gameInfo);
\r
4874 TagsPopUp(tags, CmailMsg());
\r
4877 SAY("computer starts playing black");
\r
4880 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4881 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4884 case IDM_TwoMachines:
\r
4885 TwoMachinesEvent();
\r
4887 * refresh the tags dialog only if it's visible
\r
4889 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4891 tags = PGNTags(&gameInfo);
\r
4892 TagsPopUp(tags, CmailMsg());
\r
4895 SAY("computer starts playing both sides");
\r
4898 case IDM_AnalysisMode:
\r
4899 if (!first.analysisSupport) {
\r
4900 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4901 DisplayError(buf, 0);
\r
4903 SAY("analyzing current position");
\r
4904 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4905 if (appData.icsActive) {
\r
4906 if (gameMode != IcsObserving) {
\r
4907 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4908 DisplayError(buf, 0);
\r
4909 /* secure check */
\r
4910 if (appData.icsEngineAnalyze) {
\r
4911 if (appData.debugMode)
\r
4912 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4913 ExitAnalyzeMode();
\r
4919 /* if enable, user want disable icsEngineAnalyze */
\r
4920 if (appData.icsEngineAnalyze) {
\r
4921 ExitAnalyzeMode();
\r
4925 appData.icsEngineAnalyze = TRUE;
\r
4926 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4929 if (!appData.showThinking) ToggleShowThinking();
\r
4930 AnalyzeModeEvent();
\r
4934 case IDM_AnalyzeFile:
\r
4935 if (!first.analysisSupport) {
\r
4936 char buf[MSG_SIZ];
\r
4937 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4938 DisplayError(buf, 0);
\r
4940 if (!appData.showThinking) ToggleShowThinking();
\r
4941 AnalyzeFileEvent();
\r
4942 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4943 AnalysisPeriodicEvent(1);
\r
4947 case IDM_IcsClient:
\r
4951 case IDM_EditGame:
\r
4952 case IDM_EditGame2:
\r
4957 case IDM_EditPosition:
\r
4958 case IDM_EditPosition2:
\r
4959 EditPositionEvent();
\r
4960 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4963 case IDM_Training:
\r
4967 case IDM_ShowGameList:
\r
4968 ShowGameListProc();
\r
4971 case IDM_EditProgs1:
\r
4972 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4975 case IDM_EditProgs2:
\r
4976 LoadEnginePopUp(hwndMain);
\r
4977 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4980 case IDM_EditServers:
\r
4981 EditTagsPopUp(icsNames, &icsNames);
\r
4984 case IDM_EditTags:
\r
4989 case IDM_EditBook:
\r
4993 case IDM_EditComment:
\r
4995 if (commentUp && editComment) {
\r
4998 EditCommentEvent();
\r
5018 case IDM_CallFlag:
\r
5038 case IDM_StopObserving:
\r
5039 StopObservingEvent();
\r
5042 case IDM_StopExamining:
\r
5043 StopExaminingEvent();
\r
5047 UploadGameEvent();
\r
5050 case IDM_TypeInMove:
\r
5051 TypeInEvent('\000');
\r
5054 case IDM_TypeInName:
\r
5055 PopUpNameDialog('\000');
\r
5058 case IDM_Backward:
\r
5060 SetFocus(hwndMain);
\r
5067 SetFocus(hwndMain);
\r
5072 SetFocus(hwndMain);
\r
5077 SetFocus(hwndMain);
\r
5080 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5081 case OPT_GameListPrev:
\r
5082 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5086 RevertEvent(FALSE);
\r
5089 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5090 RevertEvent(TRUE);
\r
5093 case IDM_TruncateGame:
\r
5094 TruncateGameEvent();
\r
5101 case IDM_RetractMove:
\r
5102 RetractMoveEvent();
\r
5105 case IDM_FlipView:
\r
5106 flipView = !flipView;
\r
5107 DrawPosition(FALSE, NULL);
\r
5110 case IDM_FlipClock:
\r
5111 flipClock = !flipClock;
\r
5112 DisplayBothClocks();
\r
5116 case IDM_MuteSounds:
\r
5117 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5118 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5119 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5122 case IDM_GeneralOptions:
\r
5123 GeneralOptionsPopup(hwnd);
\r
5124 DrawPosition(TRUE, NULL);
\r
5127 case IDM_BoardOptions:
\r
5128 BoardOptionsPopup(hwnd);
\r
5131 case IDM_EnginePlayOptions:
\r
5132 EnginePlayOptionsPopup(hwnd);
\r
5135 case IDM_Engine1Options:
\r
5136 EngineOptionsPopup(hwnd, &first);
\r
5139 case IDM_Engine2Options:
\r
5141 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5142 EngineOptionsPopup(hwnd, &second);
\r
5145 case IDM_OptionsUCI:
\r
5146 UciOptionsPopup(hwnd);
\r
5150 TourneyPopup(hwnd);
\r
5153 case IDM_IcsOptions:
\r
5154 IcsOptionsPopup(hwnd);
\r
5158 FontsOptionsPopup(hwnd);
\r
5162 SoundOptionsPopup(hwnd);
\r
5165 case IDM_CommPort:
\r
5166 CommPortOptionsPopup(hwnd);
\r
5169 case IDM_LoadOptions:
\r
5170 LoadOptionsPopup(hwnd);
\r
5173 case IDM_SaveOptions:
\r
5174 SaveOptionsPopup(hwnd);
\r
5177 case IDM_TimeControl:
\r
5178 TimeControlOptionsPopup(hwnd);
\r
5181 case IDM_SaveSettings:
\r
5182 SaveSettings(settingsFileName);
\r
5185 case IDM_SaveSettingsOnExit:
\r
5186 saveSettingsOnExit = !saveSettingsOnExit;
\r
5187 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5188 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5189 MF_CHECKED : MF_UNCHECKED));
\r
5200 case IDM_AboutGame:
\r
5205 appData.debugMode = !appData.debugMode;
\r
5206 if (appData.debugMode) {
\r
5207 char dir[MSG_SIZ];
\r
5208 GetCurrentDirectory(MSG_SIZ, dir);
\r
5209 SetCurrentDirectory(installDir);
\r
5210 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5211 SetCurrentDirectory(dir);
\r
5212 setbuf(debugFP, NULL);
\r
5219 case IDM_HELPCONTENTS:
\r
5220 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5221 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5222 MessageBox (GetFocus(),
\r
5223 _("Unable to activate help"),
\r
5224 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5228 case IDM_HELPSEARCH:
\r
5229 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5230 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5231 MessageBox (GetFocus(),
\r
5232 _("Unable to activate help"),
\r
5233 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5237 case IDM_HELPHELP:
\r
5238 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5239 MessageBox (GetFocus(),
\r
5240 _("Unable to activate help"),
\r
5241 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5246 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5248 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5249 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5250 FreeProcInstance(lpProc);
\r
5253 case IDM_DirectCommand1:
\r
5254 AskQuestionEvent(_("Direct Command"),
\r
5255 _("Send to chess program:"), "", "1");
\r
5257 case IDM_DirectCommand2:
\r
5258 AskQuestionEvent(_("Direct Command"),
\r
5259 _("Send to second chess program:"), "", "2");
\r
5262 case EP_WhitePawn:
\r
5263 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5264 fromX = fromY = -1;
\r
5267 case EP_WhiteKnight:
\r
5268 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5269 fromX = fromY = -1;
\r
5272 case EP_WhiteBishop:
\r
5273 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5274 fromX = fromY = -1;
\r
5277 case EP_WhiteRook:
\r
5278 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5279 fromX = fromY = -1;
\r
5282 case EP_WhiteQueen:
\r
5283 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5284 fromX = fromY = -1;
\r
5287 case EP_WhiteFerz:
\r
5288 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5289 fromX = fromY = -1;
\r
5292 case EP_WhiteWazir:
\r
5293 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5294 fromX = fromY = -1;
\r
5297 case EP_WhiteAlfil:
\r
5298 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5299 fromX = fromY = -1;
\r
5302 case EP_WhiteCannon:
\r
5303 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5304 fromX = fromY = -1;
\r
5307 case EP_WhiteCardinal:
\r
5308 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5309 fromX = fromY = -1;
\r
5312 case EP_WhiteMarshall:
\r
5313 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5314 fromX = fromY = -1;
\r
5317 case EP_WhiteKing:
\r
5318 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5319 fromX = fromY = -1;
\r
5322 case EP_BlackPawn:
\r
5323 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5324 fromX = fromY = -1;
\r
5327 case EP_BlackKnight:
\r
5328 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5329 fromX = fromY = -1;
\r
5332 case EP_BlackBishop:
\r
5333 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5334 fromX = fromY = -1;
\r
5337 case EP_BlackRook:
\r
5338 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5339 fromX = fromY = -1;
\r
5342 case EP_BlackQueen:
\r
5343 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5344 fromX = fromY = -1;
\r
5347 case EP_BlackFerz:
\r
5348 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5349 fromX = fromY = -1;
\r
5352 case EP_BlackWazir:
\r
5353 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5354 fromX = fromY = -1;
\r
5357 case EP_BlackAlfil:
\r
5358 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5362 case EP_BlackCannon:
\r
5363 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5364 fromX = fromY = -1;
\r
5367 case EP_BlackCardinal:
\r
5368 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5369 fromX = fromY = -1;
\r
5372 case EP_BlackMarshall:
\r
5373 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5374 fromX = fromY = -1;
\r
5377 case EP_BlackKing:
\r
5378 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5379 fromX = fromY = -1;
\r
5382 case EP_EmptySquare:
\r
5383 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5387 case EP_ClearBoard:
\r
5388 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5393 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5394 fromX = fromY = -1;
\r
5398 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5403 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5404 fromX = fromY = -1;
\r
5408 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5409 fromX = fromY = -1;
\r
5413 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5414 fromX = fromY = -1;
\r
5418 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5419 fromX = fromY = -1;
\r
5423 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5424 fromX = fromY = -1;
\r
5428 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5429 fromX = fromY = -1;
\r
5433 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5434 fromX = fromY = -1;
\r
5438 barbaric = 0; appData.language = "";
\r
5439 TranslateMenus(0);
\r
5440 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5441 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5442 lastChecked = wmId;
\r
5446 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5447 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5448 TranslateMenus(0);
\r
5449 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5450 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5451 lastChecked = wmId;
\r
5454 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5460 case CLOCK_TIMER_ID:
\r
5461 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5462 clockTimerEvent = 0;
\r
5463 DecrementClocks(); /* call into back end */
\r
5465 case LOAD_GAME_TIMER_ID:
\r
5466 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5467 loadGameTimerEvent = 0;
\r
5468 AutoPlayGameLoop(); /* call into back end */
\r
5470 case ANALYSIS_TIMER_ID:
\r
5471 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5472 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5473 AnalysisPeriodicEvent(0);
\r
5475 KillTimer(hwnd, analysisTimerEvent);
\r
5476 analysisTimerEvent = 0;
\r
5479 case DELAYED_TIMER_ID:
\r
5480 KillTimer(hwnd, delayedTimerEvent);
\r
5481 delayedTimerEvent = 0;
\r
5482 delayedTimerCallback();
\r
5487 case WM_USER_Input:
\r
5488 InputEvent(hwnd, message, wParam, lParam);
\r
5491 /* [AS] Also move "attached" child windows */
\r
5492 case WM_WINDOWPOSCHANGING:
\r
5494 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5495 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5497 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5498 /* Window is moving */
\r
5501 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5502 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5503 rcMain.right = wpMain.x + wpMain.width;
\r
5504 rcMain.top = wpMain.y;
\r
5505 rcMain.bottom = wpMain.y + wpMain.height;
\r
5507 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5508 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5509 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5510 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5511 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5512 wpMain.x = lpwp->x;
\r
5513 wpMain.y = lpwp->y;
\r
5518 /* [AS] Snapping */
\r
5519 case WM_ENTERSIZEMOVE:
\r
5520 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5521 if (hwnd == hwndMain) {
\r
5522 doingSizing = TRUE;
\r
5525 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5529 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5530 if (hwnd == hwndMain) {
\r
5531 lastSizing = wParam;
\r
5536 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5537 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5539 case WM_EXITSIZEMOVE:
\r
5540 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5541 if (hwnd == hwndMain) {
\r
5543 doingSizing = FALSE;
\r
5544 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5545 GetClientRect(hwnd, &client);
\r
5546 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5548 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5550 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5553 case WM_DESTROY: /* message: window being destroyed */
\r
5554 PostQuitMessage(0);
\r
5558 if (hwnd == hwndMain) {
\r
5563 default: /* Passes it on if unprocessed */
\r
5564 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5569 /*---------------------------------------------------------------------------*\
\r
5571 * Misc utility routines
\r
5573 \*---------------------------------------------------------------------------*/
\r
5576 * Decent random number generator, at least not as bad as Windows
\r
5577 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5579 unsigned int randstate;
\r
5584 randstate = randstate * 1664525 + 1013904223;
\r
5585 return (int) randstate & 0x7fffffff;
\r
5589 mysrandom(unsigned int seed)
\r
5596 * returns TRUE if user selects a different color, FALSE otherwise
\r
5600 ChangeColor(HWND hwnd, COLORREF *which)
\r
5602 static BOOL firstTime = TRUE;
\r
5603 static DWORD customColors[16];
\r
5605 COLORREF newcolor;
\r
5610 /* Make initial colors in use available as custom colors */
\r
5611 /* Should we put the compiled-in defaults here instead? */
\r
5613 customColors[i++] = lightSquareColor & 0xffffff;
\r
5614 customColors[i++] = darkSquareColor & 0xffffff;
\r
5615 customColors[i++] = whitePieceColor & 0xffffff;
\r
5616 customColors[i++] = blackPieceColor & 0xffffff;
\r
5617 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5618 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5620 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5621 customColors[i++] = textAttribs[ccl].color;
\r
5623 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5624 firstTime = FALSE;
\r
5627 cc.lStructSize = sizeof(cc);
\r
5628 cc.hwndOwner = hwnd;
\r
5629 cc.hInstance = NULL;
\r
5630 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5631 cc.lpCustColors = (LPDWORD) customColors;
\r
5632 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5634 if (!ChooseColor(&cc)) return FALSE;
\r
5636 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5637 if (newcolor == *which) return FALSE;
\r
5638 *which = newcolor;
\r
5642 InitDrawingColors();
\r
5643 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5648 MyLoadSound(MySound *ms)
\r
5654 if (ms->data && ms->flag) free(ms->data);
\r
5657 switch (ms->name[0]) {
\r
5663 /* System sound from Control Panel. Don't preload here. */
\r
5667 if (ms->name[1] == NULLCHAR) {
\r
5668 /* "!" alone = silence */
\r
5671 /* Builtin wave resource. Error if not found. */
\r
5672 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5673 if (h == NULL) break;
\r
5674 ms->data = (void *)LoadResource(hInst, h);
\r
5675 ms->flag = 0; // not maloced, so cannot be freed!
\r
5676 if (h == NULL) break;
\r
5681 /* .wav file. Error if not found. */
\r
5682 f = fopen(ms->name, "rb");
\r
5683 if (f == NULL) break;
\r
5684 if (fstat(fileno(f), &st) < 0) break;
\r
5685 ms->data = malloc(st.st_size);
\r
5687 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5693 char buf[MSG_SIZ];
\r
5694 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5695 DisplayError(buf, GetLastError());
\r
5701 MyPlaySound(MySound *ms)
\r
5703 BOOLEAN ok = FALSE;
\r
5705 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5706 switch (ms->name[0]) {
\r
5708 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5713 /* System sound from Control Panel (deprecated feature).
\r
5714 "$" alone or an unset sound name gets default beep (still in use). */
\r
5715 if (ms->name[1]) {
\r
5716 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5718 if (!ok) ok = MessageBeep(MB_OK);
\r
5721 /* Builtin wave resource, or "!" alone for silence */
\r
5722 if (ms->name[1]) {
\r
5723 if (ms->data == NULL) return FALSE;
\r
5724 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5730 /* .wav file. Error if not found. */
\r
5731 if (ms->data == NULL) return FALSE;
\r
5732 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5735 /* Don't print an error: this can happen innocently if the sound driver
\r
5736 is busy; for instance, if another instance of WinBoard is playing
\r
5737 a sound at about the same time. */
\r
5743 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5746 OPENFILENAME *ofn;
\r
5747 static UINT *number; /* gross that this is static */
\r
5749 switch (message) {
\r
5750 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5751 /* Center the dialog over the application window */
\r
5752 ofn = (OPENFILENAME *) lParam;
\r
5753 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5754 number = (UINT *) ofn->lCustData;
\r
5755 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5759 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5760 Translate(hDlg, 1536);
\r
5761 return FALSE; /* Allow for further processing */
\r
5764 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5765 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5767 return FALSE; /* Allow for further processing */
\r
5773 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5775 static UINT *number;
\r
5776 OPENFILENAME *ofname;
\r
5779 case WM_INITDIALOG:
\r
5780 Translate(hdlg, DLG_IndexNumber);
\r
5781 ofname = (OPENFILENAME *)lParam;
\r
5782 number = (UINT *)(ofname->lCustData);
\r
5785 ofnot = (OFNOTIFY *)lParam;
\r
5786 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5787 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5796 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5797 char *nameFilt, char *dlgTitle, UINT *number,
\r
5798 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5800 OPENFILENAME openFileName;
\r
5801 char buf1[MSG_SIZ];
\r
5804 if (fileName == NULL) fileName = buf1;
\r
5805 if (defName == NULL) {
\r
5806 safeStrCpy(fileName, "*.", 3 );
\r
5807 strcat(fileName, defExt);
\r
5809 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5811 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5812 if (number) *number = 0;
\r
5814 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5815 openFileName.hwndOwner = hwnd;
\r
5816 openFileName.hInstance = (HANDLE) hInst;
\r
5817 openFileName.lpstrFilter = nameFilt;
\r
5818 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5819 openFileName.nMaxCustFilter = 0L;
\r
5820 openFileName.nFilterIndex = 1L;
\r
5821 openFileName.lpstrFile = fileName;
\r
5822 openFileName.nMaxFile = MSG_SIZ;
\r
5823 openFileName.lpstrFileTitle = fileTitle;
\r
5824 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5825 openFileName.lpstrInitialDir = NULL;
\r
5826 openFileName.lpstrTitle = dlgTitle;
\r
5827 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5828 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5829 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5830 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5831 openFileName.nFileOffset = 0;
\r
5832 openFileName.nFileExtension = 0;
\r
5833 openFileName.lpstrDefExt = defExt;
\r
5834 openFileName.lCustData = (LONG) number;
\r
5835 openFileName.lpfnHook = oldDialog ?
\r
5836 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5837 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5839 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5840 GetOpenFileName(&openFileName)) {
\r
5841 /* open the file */
\r
5842 f = fopen(openFileName.lpstrFile, write);
\r
5844 MessageBox(hwnd, _("File open failed"), NULL,
\r
5845 MB_OK|MB_ICONEXCLAMATION);
\r
5849 int err = CommDlgExtendedError();
\r
5850 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5859 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5861 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5864 * Get the first pop-up menu in the menu template. This is the
\r
5865 * menu that TrackPopupMenu displays.
\r
5867 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5868 TranslateOneMenu(10, hmenuTrackPopup);
\r
5870 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5873 * TrackPopup uses screen coordinates, so convert the
\r
5874 * coordinates of the mouse click to screen coordinates.
\r
5876 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5878 /* Draw and track the floating pop-up menu. */
\r
5879 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5880 pt.x, pt.y, 0, hwnd, NULL);
\r
5882 /* Destroy the menu.*/
\r
5883 DestroyMenu(hmenu);
\r
5888 int sizeX, sizeY, newSizeX, newSizeY;
\r
5890 } ResizeEditPlusButtonsClosure;
\r
5893 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5895 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5899 if (hChild == cl->hText) return TRUE;
\r
5900 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5901 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5902 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5903 ScreenToClient(cl->hDlg, &pt);
\r
5904 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5905 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5909 /* Resize a dialog that has a (rich) edit field filling most of
\r
5910 the top, with a row of buttons below */
\r
5912 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5915 int newTextHeight, newTextWidth;
\r
5916 ResizeEditPlusButtonsClosure cl;
\r
5918 /*if (IsIconic(hDlg)) return;*/
\r
5919 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5921 cl.hdwp = BeginDeferWindowPos(8);
\r
5923 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5924 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5925 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5926 if (newTextHeight < 0) {
\r
5927 newSizeY += -newTextHeight;
\r
5928 newTextHeight = 0;
\r
5930 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5931 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5937 cl.newSizeX = newSizeX;
\r
5938 cl.newSizeY = newSizeY;
\r
5939 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5941 EndDeferWindowPos(cl.hdwp);
\r
5944 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5946 RECT rChild, rParent;
\r
5947 int wChild, hChild, wParent, hParent;
\r
5948 int wScreen, hScreen, xNew, yNew;
\r
5951 /* Get the Height and Width of the child window */
\r
5952 GetWindowRect (hwndChild, &rChild);
\r
5953 wChild = rChild.right - rChild.left;
\r
5954 hChild = rChild.bottom - rChild.top;
\r
5956 /* Get the Height and Width of the parent window */
\r
5957 GetWindowRect (hwndParent, &rParent);
\r
5958 wParent = rParent.right - rParent.left;
\r
5959 hParent = rParent.bottom - rParent.top;
\r
5961 /* Get the display limits */
\r
5962 hdc = GetDC (hwndChild);
\r
5963 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5964 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5965 ReleaseDC(hwndChild, hdc);
\r
5967 /* Calculate new X position, then adjust for screen */
\r
5968 xNew = rParent.left + ((wParent - wChild) /2);
\r
5971 } else if ((xNew+wChild) > wScreen) {
\r
5972 xNew = wScreen - wChild;
\r
5975 /* Calculate new Y position, then adjust for screen */
\r
5977 yNew = rParent.top + ((hParent - hChild) /2);
\r
5980 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5985 } else if ((yNew+hChild) > hScreen) {
\r
5986 yNew = hScreen - hChild;
\r
5989 /* Set it, and return */
\r
5990 return SetWindowPos (hwndChild, NULL,
\r
5991 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5994 /* Center one window over another */
\r
5995 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5997 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6000 /*---------------------------------------------------------------------------*\
\r
6002 * Startup Dialog functions
\r
6004 \*---------------------------------------------------------------------------*/
\r
6006 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6008 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6010 while (*cd != NULL) {
\r
6011 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6017 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6019 char buf1[MAX_ARG_LEN];
\r
6022 if (str[0] == '@') {
\r
6023 FILE* f = fopen(str + 1, "r");
\r
6025 DisplayFatalError(str + 1, errno, 2);
\r
6028 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6030 buf1[len] = NULLCHAR;
\r
6034 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6037 char buf[MSG_SIZ];
\r
6038 char *end = strchr(str, '\n');
\r
6039 if (end == NULL) return;
\r
6040 memcpy(buf, str, end - str);
\r
6041 buf[end - str] = NULLCHAR;
\r
6042 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6048 SetStartupDialogEnables(HWND hDlg)
\r
6050 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6051 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6052 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6053 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6054 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6055 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6056 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6057 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6058 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6059 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6060 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6061 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6062 IsDlgButtonChecked(hDlg, OPT_View));
\r
6066 QuoteForFilename(char *filename)
\r
6068 int dquote, space;
\r
6069 dquote = strchr(filename, '"') != NULL;
\r
6070 space = strchr(filename, ' ') != NULL;
\r
6071 if (dquote || space) {
\r
6083 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6085 char buf[MSG_SIZ];
\r
6088 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6089 q = QuoteForFilename(nthcp);
\r
6090 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6091 if (*nthdir != NULLCHAR) {
\r
6092 q = QuoteForFilename(nthdir);
\r
6093 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6095 if (*nthcp == NULLCHAR) {
\r
6096 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6097 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6098 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6099 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6104 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6106 char buf[MSG_SIZ];
\r
6110 switch (message) {
\r
6111 case WM_INITDIALOG:
\r
6112 /* Center the dialog */
\r
6113 CenterWindow (hDlg, GetDesktopWindow());
\r
6114 Translate(hDlg, DLG_Startup);
\r
6115 /* Initialize the dialog items */
\r
6116 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6117 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6118 firstChessProgramNames);
\r
6119 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6120 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6121 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6122 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6123 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6124 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6125 if (*appData.icsHelper != NULLCHAR) {
\r
6126 char *q = QuoteForFilename(appData.icsHelper);
\r
6127 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6129 if (*appData.icsHost == NULLCHAR) {
\r
6130 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6131 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6132 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6133 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6134 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6137 if (appData.icsActive) {
\r
6138 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6140 else if (appData.noChessProgram) {
\r
6141 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6144 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6147 SetStartupDialogEnables(hDlg);
\r
6151 switch (LOWORD(wParam)) {
\r
6153 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6154 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6155 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6157 ParseArgs(StringGet, &p);
\r
6158 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6159 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6161 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6162 ParseArgs(StringGet, &p);
\r
6163 SwapEngines(singleList); // ... and then make it 'second'
\r
6164 appData.noChessProgram = FALSE;
\r
6165 appData.icsActive = FALSE;
\r
6166 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6167 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6168 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6170 ParseArgs(StringGet, &p);
\r
6171 if (appData.zippyPlay) {
\r
6172 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6173 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6175 ParseArgs(StringGet, &p);
\r
6177 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6178 appData.noChessProgram = TRUE;
\r
6179 appData.icsActive = FALSE;
\r
6181 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6182 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6185 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6186 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6188 ParseArgs(StringGet, &p);
\r
6190 EndDialog(hDlg, TRUE);
\r
6197 case IDM_HELPCONTENTS:
\r
6198 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6199 MessageBox (GetFocus(),
\r
6200 _("Unable to activate help"),
\r
6201 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6206 SetStartupDialogEnables(hDlg);
\r
6214 /*---------------------------------------------------------------------------*\
\r
6216 * About box dialog functions
\r
6218 \*---------------------------------------------------------------------------*/
\r
6220 /* Process messages for "About" dialog box */
\r
6222 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6224 switch (message) {
\r
6225 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6226 /* Center the dialog over the application window */
\r
6227 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6228 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6229 Translate(hDlg, ABOUTBOX);
\r
6233 case WM_COMMAND: /* message: received a command */
\r
6234 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6235 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6236 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6244 /*---------------------------------------------------------------------------*\
\r
6246 * Comment Dialog functions
\r
6248 \*---------------------------------------------------------------------------*/
\r
6251 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6253 static HANDLE hwndText = NULL;
\r
6254 int len, newSizeX, newSizeY, flags;
\r
6255 static int sizeX, sizeY;
\r
6260 switch (message) {
\r
6261 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6262 /* Initialize the dialog items */
\r
6263 Translate(hDlg, DLG_EditComment);
\r
6264 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6265 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6266 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6267 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6268 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6269 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6270 SetWindowText(hDlg, commentTitle);
\r
6271 if (editComment) {
\r
6272 SetFocus(hwndText);
\r
6274 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6276 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6277 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6278 MAKELPARAM(FALSE, 0));
\r
6279 /* Size and position the dialog */
\r
6280 if (!commentDialog) {
\r
6281 commentDialog = hDlg;
\r
6282 flags = SWP_NOZORDER;
\r
6283 GetClientRect(hDlg, &rect);
\r
6284 sizeX = rect.right;
\r
6285 sizeY = rect.bottom;
\r
6286 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6287 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6288 WINDOWPLACEMENT wp;
\r
6289 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6290 wp.length = sizeof(WINDOWPLACEMENT);
\r
6292 wp.showCmd = SW_SHOW;
\r
6293 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6294 wp.rcNormalPosition.left = wpComment.x;
\r
6295 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6296 wp.rcNormalPosition.top = wpComment.y;
\r
6297 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6298 SetWindowPlacement(hDlg, &wp);
\r
6300 GetClientRect(hDlg, &rect);
\r
6301 newSizeX = rect.right;
\r
6302 newSizeY = rect.bottom;
\r
6303 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6304 newSizeX, newSizeY);
\r
6309 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6312 case WM_COMMAND: /* message: received a command */
\r
6313 switch (LOWORD(wParam)) {
\r
6315 if (editComment) {
\r
6317 /* Read changed options from the dialog box */
\r
6318 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6319 len = GetWindowTextLength(hwndText);
\r
6320 str = (char *) malloc(len + 1);
\r
6321 GetWindowText(hwndText, str, len + 1);
\r
6330 ReplaceComment(commentIndex, str);
\r
6337 case OPT_CancelComment:
\r
6341 case OPT_ClearComment:
\r
6342 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6345 case OPT_EditComment:
\r
6346 EditCommentEvent();
\r
6354 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6355 if( wParam == OPT_CommentText ) {
\r
6356 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6358 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6359 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6363 pt.x = LOWORD( lpMF->lParam );
\r
6364 pt.y = HIWORD( lpMF->lParam );
\r
6366 if(lpMF->msg == WM_CHAR) {
\r
6368 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6369 index = sel.cpMin;
\r
6371 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6373 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6374 len = GetWindowTextLength(hwndText);
\r
6375 str = (char *) malloc(len + 1);
\r
6376 GetWindowText(hwndText, str, len + 1);
\r
6377 ReplaceComment(commentIndex, str);
\r
6378 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6379 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6382 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6383 lpMF->msg = WM_USER;
\r
6391 newSizeX = LOWORD(lParam);
\r
6392 newSizeY = HIWORD(lParam);
\r
6393 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6398 case WM_GETMINMAXINFO:
\r
6399 /* Prevent resizing window too small */
\r
6400 mmi = (MINMAXINFO *) lParam;
\r
6401 mmi->ptMinTrackSize.x = 100;
\r
6402 mmi->ptMinTrackSize.y = 100;
\r
6409 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6414 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6416 if (str == NULL) str = "";
\r
6417 p = (char *) malloc(2 * strlen(str) + 2);
\r
6420 if (*str == '\n') *q++ = '\r';
\r
6424 if (commentText != NULL) free(commentText);
\r
6426 commentIndex = index;
\r
6427 commentTitle = title;
\r
6429 editComment = edit;
\r
6431 if (commentDialog) {
\r
6432 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6433 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6435 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6436 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6437 hwndMain, (DLGPROC)lpProc);
\r
6438 FreeProcInstance(lpProc);
\r
6444 /*---------------------------------------------------------------------------*\
\r
6446 * Type-in move dialog functions
\r
6448 \*---------------------------------------------------------------------------*/
\r
6451 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6453 char move[MSG_SIZ];
\r
6456 switch (message) {
\r
6457 case WM_INITDIALOG:
\r
6458 move[0] = (char) lParam;
\r
6459 move[1] = NULLCHAR;
\r
6460 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6461 Translate(hDlg, DLG_TypeInMove);
\r
6462 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6463 SetWindowText(hInput, move);
\r
6465 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6469 switch (LOWORD(wParam)) {
\r
6472 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6473 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6474 TypeInDoneEvent(move);
\r
6475 EndDialog(hDlg, TRUE);
\r
6478 EndDialog(hDlg, FALSE);
\r
6489 PopUpMoveDialog(char firstchar)
\r
6493 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6494 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6495 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6496 FreeProcInstance(lpProc);
\r
6499 /*---------------------------------------------------------------------------*\
\r
6501 * Type-in name dialog functions
\r
6503 \*---------------------------------------------------------------------------*/
\r
6506 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6508 char move[MSG_SIZ];
\r
6511 switch (message) {
\r
6512 case WM_INITDIALOG:
\r
6513 move[0] = (char) lParam;
\r
6514 move[1] = NULLCHAR;
\r
6515 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6516 Translate(hDlg, DLG_TypeInName);
\r
6517 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6518 SetWindowText(hInput, move);
\r
6520 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6524 switch (LOWORD(wParam)) {
\r
6526 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6527 appData.userName = strdup(move);
\r
6530 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6531 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6532 DisplayTitle(move);
\r
6536 EndDialog(hDlg, TRUE);
\r
6539 EndDialog(hDlg, FALSE);
\r
6550 PopUpNameDialog(char firstchar)
\r
6554 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6555 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6556 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6557 FreeProcInstance(lpProc);
\r
6560 /*---------------------------------------------------------------------------*\
\r
6564 \*---------------------------------------------------------------------------*/
\r
6566 /* Nonmodal error box */
\r
6567 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6568 WPARAM wParam, LPARAM lParam);
\r
6571 ErrorPopUp(char *title, char *content)
\r
6575 BOOLEAN modal = hwndMain == NULL;
\r
6593 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6594 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6597 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6599 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6600 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6601 hwndMain, (DLGPROC)lpProc);
\r
6602 FreeProcInstance(lpProc);
\r
6609 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6610 if (errorDialog == NULL) return;
\r
6611 DestroyWindow(errorDialog);
\r
6612 errorDialog = NULL;
\r
6613 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6617 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6622 switch (message) {
\r
6623 case WM_INITDIALOG:
\r
6624 GetWindowRect(hDlg, &rChild);
\r
6627 SetWindowPos(hDlg, NULL, rChild.left,
\r
6628 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6629 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6633 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6634 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6635 and it doesn't work when you resize the dialog.
\r
6636 For now, just give it a default position.
\r
6638 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6639 Translate(hDlg, DLG_Error);
\r
6641 errorDialog = hDlg;
\r
6642 SetWindowText(hDlg, errorTitle);
\r
6643 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6644 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6648 switch (LOWORD(wParam)) {
\r
6651 if (errorDialog == hDlg) errorDialog = NULL;
\r
6652 DestroyWindow(hDlg);
\r
6664 HWND gothicDialog = NULL;
\r
6667 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6671 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6673 switch (message) {
\r
6674 case WM_INITDIALOG:
\r
6675 GetWindowRect(hDlg, &rChild);
\r
6677 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6681 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6682 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6683 and it doesn't work when you resize the dialog.
\r
6684 For now, just give it a default position.
\r
6686 gothicDialog = hDlg;
\r
6687 SetWindowText(hDlg, errorTitle);
\r
6688 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6689 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6693 switch (LOWORD(wParam)) {
\r
6696 if (errorDialog == hDlg) errorDialog = NULL;
\r
6697 DestroyWindow(hDlg);
\r
6709 GothicPopUp(char *title, VariantClass variant)
\r
6712 static char *lastTitle;
\r
6714 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6715 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6717 if(lastTitle != title && gothicDialog != NULL) {
\r
6718 DestroyWindow(gothicDialog);
\r
6719 gothicDialog = NULL;
\r
6721 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6722 title = lastTitle;
\r
6723 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6724 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6725 hwndMain, (DLGPROC)lpProc);
\r
6726 FreeProcInstance(lpProc);
\r
6731 /*---------------------------------------------------------------------------*\
\r
6733 * Ics Interaction console functions
\r
6735 \*---------------------------------------------------------------------------*/
\r
6737 #define HISTORY_SIZE 64
\r
6738 static char *history[HISTORY_SIZE];
\r
6739 int histIn = 0, histP = 0;
\r
6742 SaveInHistory(char *cmd)
\r
6744 if (history[histIn] != NULL) {
\r
6745 free(history[histIn]);
\r
6746 history[histIn] = NULL;
\r
6748 if (*cmd == NULLCHAR) return;
\r
6749 history[histIn] = StrSave(cmd);
\r
6750 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6751 if (history[histIn] != NULL) {
\r
6752 free(history[histIn]);
\r
6753 history[histIn] = NULL;
\r
6759 PrevInHistory(char *cmd)
\r
6762 if (histP == histIn) {
\r
6763 if (history[histIn] != NULL) free(history[histIn]);
\r
6764 history[histIn] = StrSave(cmd);
\r
6766 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6767 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6769 return history[histP];
\r
6775 if (histP == histIn) return NULL;
\r
6776 histP = (histP + 1) % HISTORY_SIZE;
\r
6777 return history[histP];
\r
6781 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6785 hmenu = LoadMenu(hInst, "TextMenu");
\r
6786 h = GetSubMenu(hmenu, 0);
\r
6788 if (strcmp(e->item, "-") == 0) {
\r
6789 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6790 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6791 int flags = MF_STRING, j = 0;
\r
6792 if (e->item[0] == '|') {
\r
6793 flags |= MF_MENUBARBREAK;
\r
6796 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6797 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6805 WNDPROC consoleTextWindowProc;
\r
6808 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6810 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6811 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6815 SetWindowText(hInput, command);
\r
6817 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6819 sel.cpMin = 999999;
\r
6820 sel.cpMax = 999999;
\r
6821 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6826 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6827 if (sel.cpMin == sel.cpMax) {
\r
6828 /* Expand to surrounding word */
\r
6831 tr.chrg.cpMax = sel.cpMin;
\r
6832 tr.chrg.cpMin = --sel.cpMin;
\r
6833 if (sel.cpMin < 0) break;
\r
6834 tr.lpstrText = name;
\r
6835 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6836 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6840 tr.chrg.cpMin = sel.cpMax;
\r
6841 tr.chrg.cpMax = ++sel.cpMax;
\r
6842 tr.lpstrText = name;
\r
6843 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6844 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6847 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6848 MessageBeep(MB_ICONEXCLAMATION);
\r
6852 tr.lpstrText = name;
\r
6853 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6855 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6856 MessageBeep(MB_ICONEXCLAMATION);
\r
6859 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6862 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6863 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6864 SetWindowText(hInput, buf);
\r
6865 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6867 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6868 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6869 SetWindowText(hInput, buf);
\r
6870 sel.cpMin = 999999;
\r
6871 sel.cpMax = 999999;
\r
6872 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6878 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6883 switch (message) {
\r
6885 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6886 if(wParam=='R') return 0;
\r
6889 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6892 sel.cpMin = 999999;
\r
6893 sel.cpMax = 999999;
\r
6894 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6895 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6900 if(wParam != '\022') {
\r
6901 if (wParam == '\t') {
\r
6902 if (GetKeyState(VK_SHIFT) < 0) {
\r
6904 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6905 if (buttonDesc[0].hwnd) {
\r
6906 SetFocus(buttonDesc[0].hwnd);
\r
6908 SetFocus(hwndMain);
\r
6912 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6915 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6916 JAWS_DELETE( SetFocus(hInput); )
\r
6917 SendMessage(hInput, message, wParam, lParam);
\r
6920 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6922 case WM_RBUTTONDOWN:
\r
6923 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6924 /* Move selection here if it was empty */
\r
6926 pt.x = LOWORD(lParam);
\r
6927 pt.y = HIWORD(lParam);
\r
6928 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6929 if (sel.cpMin == sel.cpMax) {
\r
6930 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6931 sel.cpMax = sel.cpMin;
\r
6932 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6934 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6935 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6937 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6938 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6939 if (sel.cpMin == sel.cpMax) {
\r
6940 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6941 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6943 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6944 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6946 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6947 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6948 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6949 MenuPopup(hwnd, pt, hmenu, -1);
\r
6953 case WM_RBUTTONUP:
\r
6954 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6955 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6956 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6960 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6962 return SendMessage(hInput, message, wParam, lParam);
\r
6963 case WM_MBUTTONDOWN:
\r
6964 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6966 switch (LOWORD(wParam)) {
\r
6967 case IDM_QuickPaste:
\r
6969 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6970 if (sel.cpMin == sel.cpMax) {
\r
6971 MessageBeep(MB_ICONEXCLAMATION);
\r
6974 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6975 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6976 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6981 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6984 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6987 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6991 int i = LOWORD(wParam) - IDM_CommandX;
\r
6992 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6993 icsTextMenuEntry[i].command != NULL) {
\r
6994 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6995 icsTextMenuEntry[i].getname,
\r
6996 icsTextMenuEntry[i].immediate);
\r
7004 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7007 WNDPROC consoleInputWindowProc;
\r
7010 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7012 char buf[MSG_SIZ];
\r
7014 static BOOL sendNextChar = FALSE;
\r
7015 static BOOL quoteNextChar = FALSE;
\r
7016 InputSource *is = consoleInputSource;
\r
7020 switch (message) {
\r
7022 if (!appData.localLineEditing || sendNextChar) {
\r
7023 is->buf[0] = (CHAR) wParam;
\r
7025 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7026 sendNextChar = FALSE;
\r
7029 if (quoteNextChar) {
\r
7030 buf[0] = (char) wParam;
\r
7031 buf[1] = NULLCHAR;
\r
7032 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7033 quoteNextChar = FALSE;
\r
7037 case '\r': /* Enter key */
\r
7038 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7039 if (consoleEcho) SaveInHistory(is->buf);
\r
7040 is->buf[is->count++] = '\n';
\r
7041 is->buf[is->count] = NULLCHAR;
\r
7042 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7043 if (consoleEcho) {
\r
7044 ConsoleOutput(is->buf, is->count, TRUE);
\r
7045 } else if (appData.localLineEditing) {
\r
7046 ConsoleOutput("\n", 1, TRUE);
\r
7049 case '\033': /* Escape key */
\r
7050 SetWindowText(hwnd, "");
\r
7051 cf.cbSize = sizeof(CHARFORMAT);
\r
7052 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7053 if (consoleEcho) {
\r
7054 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7056 cf.crTextColor = COLOR_ECHOOFF;
\r
7058 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7059 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7061 case '\t': /* Tab key */
\r
7062 if (GetKeyState(VK_SHIFT) < 0) {
\r
7064 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7067 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7068 if (buttonDesc[0].hwnd) {
\r
7069 SetFocus(buttonDesc[0].hwnd);
\r
7071 SetFocus(hwndMain);
\r
7075 case '\023': /* Ctrl+S */
\r
7076 sendNextChar = TRUE;
\r
7078 case '\021': /* Ctrl+Q */
\r
7079 quoteNextChar = TRUE;
\r
7089 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7090 p = PrevInHistory(buf);
\r
7092 SetWindowText(hwnd, p);
\r
7093 sel.cpMin = 999999;
\r
7094 sel.cpMax = 999999;
\r
7095 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7100 p = NextInHistory();
\r
7102 SetWindowText(hwnd, p);
\r
7103 sel.cpMin = 999999;
\r
7104 sel.cpMax = 999999;
\r
7105 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7111 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7115 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7119 case WM_MBUTTONDOWN:
\r
7120 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7121 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7123 case WM_RBUTTONUP:
\r
7124 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7125 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7126 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7130 hmenu = LoadMenu(hInst, "InputMenu");
\r
7131 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7132 if (sel.cpMin == sel.cpMax) {
\r
7133 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7134 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7136 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7137 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7139 pt.x = LOWORD(lParam);
\r
7140 pt.y = HIWORD(lParam);
\r
7141 MenuPopup(hwnd, pt, hmenu, -1);
\r
7145 switch (LOWORD(wParam)) {
\r
7147 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7149 case IDM_SelectAll:
\r
7151 sel.cpMax = -1; /*999999?*/
\r
7152 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7155 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7158 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7161 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7166 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7169 #define CO_MAX 100000
\r
7170 #define CO_TRIM 1000
\r
7173 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7175 static SnapData sd;
\r
7176 HWND hText, hInput;
\r
7178 static int sizeX, sizeY;
\r
7179 int newSizeX, newSizeY;
\r
7183 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7184 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7186 switch (message) {
\r
7188 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7190 ENLINK *pLink = (ENLINK*)lParam;
\r
7191 if (pLink->msg == WM_LBUTTONUP)
\r
7195 tr.chrg = pLink->chrg;
\r
7196 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7197 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7198 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7199 free(tr.lpstrText);
\r
7203 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7204 hwndConsole = hDlg;
\r
7206 consoleTextWindowProc = (WNDPROC)
\r
7207 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7208 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7209 consoleInputWindowProc = (WNDPROC)
\r
7210 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7211 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7212 Colorize(ColorNormal, TRUE);
\r
7213 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7214 ChangedConsoleFont();
\r
7215 GetClientRect(hDlg, &rect);
\r
7216 sizeX = rect.right;
\r
7217 sizeY = rect.bottom;
\r
7218 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7219 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7220 WINDOWPLACEMENT wp;
\r
7221 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7222 wp.length = sizeof(WINDOWPLACEMENT);
\r
7224 wp.showCmd = SW_SHOW;
\r
7225 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7226 wp.rcNormalPosition.left = wpConsole.x;
\r
7227 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7228 wp.rcNormalPosition.top = wpConsole.y;
\r
7229 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7230 SetWindowPlacement(hDlg, &wp);
\r
7233 // [HGM] Chessknight's change 2004-07-13
\r
7234 else { /* Determine Defaults */
\r
7235 WINDOWPLACEMENT wp;
\r
7236 wpConsole.x = wpMain.width + 1;
\r
7237 wpConsole.y = wpMain.y;
\r
7238 wpConsole.width = screenWidth - wpMain.width;
\r
7239 wpConsole.height = wpMain.height;
\r
7240 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7241 wp.length = sizeof(WINDOWPLACEMENT);
\r
7243 wp.showCmd = SW_SHOW;
\r
7244 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7245 wp.rcNormalPosition.left = wpConsole.x;
\r
7246 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7247 wp.rcNormalPosition.top = wpConsole.y;
\r
7248 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7249 SetWindowPlacement(hDlg, &wp);
\r
7252 // Allow hText to highlight URLs and send notifications on them
\r
7253 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7254 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7255 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7256 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7270 if (IsIconic(hDlg)) break;
\r
7271 newSizeX = LOWORD(lParam);
\r
7272 newSizeY = HIWORD(lParam);
\r
7273 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7274 RECT rectText, rectInput;
\r
7276 int newTextHeight, newTextWidth;
\r
7277 GetWindowRect(hText, &rectText);
\r
7278 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7279 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7280 if (newTextHeight < 0) {
\r
7281 newSizeY += -newTextHeight;
\r
7282 newTextHeight = 0;
\r
7284 SetWindowPos(hText, NULL, 0, 0,
\r
7285 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7286 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7287 pt.x = rectInput.left;
\r
7288 pt.y = rectInput.top + newSizeY - sizeY;
\r
7289 ScreenToClient(hDlg, &pt);
\r
7290 SetWindowPos(hInput, NULL,
\r
7291 pt.x, pt.y, /* needs client coords */
\r
7292 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7293 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7299 case WM_GETMINMAXINFO:
\r
7300 /* Prevent resizing window too small */
\r
7301 mmi = (MINMAXINFO *) lParam;
\r
7302 mmi->ptMinTrackSize.x = 100;
\r
7303 mmi->ptMinTrackSize.y = 100;
\r
7306 /* [AS] Snapping */
\r
7307 case WM_ENTERSIZEMOVE:
\r
7308 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7311 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7314 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7316 case WM_EXITSIZEMOVE:
\r
7317 UpdateICSWidth(hText);
\r
7318 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7321 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7329 if (hwndConsole) return;
\r
7330 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7331 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7336 ConsoleOutput(char* data, int length, int forceVisible)
\r
7341 char buf[CO_MAX+1];
\r
7344 static int delayLF = 0;
\r
7345 CHARRANGE savesel, sel;
\r
7347 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7355 while (length--) {
\r
7363 } else if (*p == '\007') {
\r
7364 MyPlaySound(&sounds[(int)SoundBell]);
\r
7371 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7372 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7373 /* Save current selection */
\r
7374 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7375 exlen = GetWindowTextLength(hText);
\r
7376 /* Find out whether current end of text is visible */
\r
7377 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7378 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7379 /* Trim existing text if it's too long */
\r
7380 if (exlen + (q - buf) > CO_MAX) {
\r
7381 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7384 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7385 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7387 savesel.cpMin -= trim;
\r
7388 savesel.cpMax -= trim;
\r
7389 if (exlen < 0) exlen = 0;
\r
7390 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7391 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7393 /* Append the new text */
\r
7394 sel.cpMin = exlen;
\r
7395 sel.cpMax = exlen;
\r
7396 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7397 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7398 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7399 if (forceVisible || exlen == 0 ||
\r
7400 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7401 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7402 /* Scroll to make new end of text visible if old end of text
\r
7403 was visible or new text is an echo of user typein */
\r
7404 sel.cpMin = 9999999;
\r
7405 sel.cpMax = 9999999;
\r
7406 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7407 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7408 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7409 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7411 if (savesel.cpMax == exlen || forceVisible) {
\r
7412 /* Move insert point to new end of text if it was at the old
\r
7413 end of text or if the new text is an echo of user typein */
\r
7414 sel.cpMin = 9999999;
\r
7415 sel.cpMax = 9999999;
\r
7416 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7418 /* Restore previous selection */
\r
7419 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7421 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7428 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7432 COLORREF oldFg, oldBg;
\r
7437 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7439 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7440 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7441 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7444 rect.right = x + squareSize;
\r
7446 rect.bottom = y + squareSize;
\r
7449 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7450 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7451 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7452 &rect, str, strlen(str), NULL);
\r
7454 (void) SetTextColor(hdc, oldFg);
\r
7455 (void) SetBkColor(hdc, oldBg);
\r
7456 (void) SelectObject(hdc, oldFont);
\r
7460 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7461 RECT *rect, char *color, char *flagFell)
\r
7465 COLORREF oldFg, oldBg;
\r
7468 if (appData.clockMode) {
\r
7470 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7472 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7479 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7480 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7482 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7483 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7485 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7489 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7490 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7491 rect, str, strlen(str), NULL);
\r
7492 if(logoHeight > 0 && appData.clockMode) {
\r
7494 str += strlen(color)+2;
\r
7495 r.top = rect->top + logoHeight/2;
\r
7496 r.left = rect->left;
\r
7497 r.right = rect->right;
\r
7498 r.bottom = rect->bottom;
\r
7499 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7500 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7501 &r, str, strlen(str), NULL);
\r
7503 (void) SetTextColor(hdc, oldFg);
\r
7504 (void) SetBkColor(hdc, oldBg);
\r
7505 (void) SelectObject(hdc, oldFont);
\r
7510 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7516 if( count <= 0 ) {
\r
7517 if (appData.debugMode) {
\r
7518 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7521 return ERROR_INVALID_USER_BUFFER;
\r
7524 ResetEvent(ovl->hEvent);
\r
7525 ovl->Offset = ovl->OffsetHigh = 0;
\r
7526 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7530 err = GetLastError();
\r
7531 if (err == ERROR_IO_PENDING) {
\r
7532 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7536 err = GetLastError();
\r
7543 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7548 ResetEvent(ovl->hEvent);
\r
7549 ovl->Offset = ovl->OffsetHigh = 0;
\r
7550 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7554 err = GetLastError();
\r
7555 if (err == ERROR_IO_PENDING) {
\r
7556 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7560 err = GetLastError();
\r
7566 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7567 void CheckForInputBufferFull( InputSource * is )
\r
7569 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7570 /* Look for end of line */
\r
7571 char * p = is->buf;
\r
7573 while( p < is->next && *p != '\n' ) {
\r
7577 if( p >= is->next ) {
\r
7578 if (appData.debugMode) {
\r
7579 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7582 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7583 is->count = (DWORD) -1;
\r
7584 is->next = is->buf;
\r
7590 InputThread(LPVOID arg)
\r
7595 is = (InputSource *) arg;
\r
7596 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7597 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7598 while (is->hThread != NULL) {
\r
7599 is->error = DoReadFile(is->hFile, is->next,
\r
7600 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7601 &is->count, &ovl);
\r
7602 if (is->error == NO_ERROR) {
\r
7603 is->next += is->count;
\r
7605 if (is->error == ERROR_BROKEN_PIPE) {
\r
7606 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7609 is->count = (DWORD) -1;
\r
7610 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7615 CheckForInputBufferFull( is );
\r
7617 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7619 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7621 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7624 CloseHandle(ovl.hEvent);
\r
7625 CloseHandle(is->hFile);
\r
7627 if (appData.debugMode) {
\r
7628 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7635 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7637 NonOvlInputThread(LPVOID arg)
\r
7644 is = (InputSource *) arg;
\r
7645 while (is->hThread != NULL) {
\r
7646 is->error = ReadFile(is->hFile, is->next,
\r
7647 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7648 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7649 if (is->error == NO_ERROR) {
\r
7650 /* Change CRLF to LF */
\r
7651 if (is->next > is->buf) {
\r
7653 i = is->count + 1;
\r
7661 if (prev == '\r' && *p == '\n') {
\r
7673 if (is->error == ERROR_BROKEN_PIPE) {
\r
7674 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7677 is->count = (DWORD) -1;
\r
7681 CheckForInputBufferFull( is );
\r
7683 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7685 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7687 if (is->count < 0) break; /* Quit on error */
\r
7689 CloseHandle(is->hFile);
\r
7694 SocketInputThread(LPVOID arg)
\r
7698 is = (InputSource *) arg;
\r
7699 while (is->hThread != NULL) {
\r
7700 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7701 if ((int)is->count == SOCKET_ERROR) {
\r
7702 is->count = (DWORD) -1;
\r
7703 is->error = WSAGetLastError();
\r
7705 is->error = NO_ERROR;
\r
7706 is->next += is->count;
\r
7707 if (is->count == 0 && is->second == is) {
\r
7708 /* End of file on stderr; quit with no message */
\r
7712 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7714 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7716 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7722 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7726 is = (InputSource *) lParam;
\r
7727 if (is->lineByLine) {
\r
7728 /* Feed in lines one by one */
\r
7729 char *p = is->buf;
\r
7731 while (q < is->next) {
\r
7732 if (*q++ == '\n') {
\r
7733 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7738 /* Move any partial line to the start of the buffer */
\r
7740 while (p < is->next) {
\r
7745 if (is->error != NO_ERROR || is->count == 0) {
\r
7746 /* Notify backend of the error. Note: If there was a partial
\r
7747 line at the end, it is not flushed through. */
\r
7748 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7751 /* Feed in the whole chunk of input at once */
\r
7752 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7753 is->next = is->buf;
\r
7757 /*---------------------------------------------------------------------------*\
\r
7759 * Menu enables. Used when setting various modes.
\r
7761 \*---------------------------------------------------------------------------*/
\r
7769 GreyRevert(Boolean grey)
\r
7770 { // [HGM] vari: for retracting variations in local mode
\r
7771 HMENU hmenu = GetMenu(hwndMain);
\r
7772 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7773 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7777 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7779 while (enab->item > 0) {
\r
7780 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7785 Enables gnuEnables[] = {
\r
7786 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7800 // Needed to switch from ncp to GNU mode on Engine Load
\r
7801 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7802 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7803 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7804 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7806 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7807 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7808 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7809 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7810 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7811 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7812 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7813 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7814 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7818 Enables icsEnables[] = {
\r
7819 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7834 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7842 Enables zippyEnables[] = {
\r
7843 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7844 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7846 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7851 Enables ncpEnables[] = {
\r
7852 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7862 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7877 Enables trainingOnEnables[] = {
\r
7878 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7890 Enables trainingOffEnables[] = {
\r
7891 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7892 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7893 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7894 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7895 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7896 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7899 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7903 /* These modify either ncpEnables or gnuEnables */
\r
7904 Enables cmailEnables[] = {
\r
7905 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7908 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7915 Enables machineThinkingEnables[] = {
\r
7916 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7927 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7929 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7935 Enables userThinkingEnables[] = {
\r
7936 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7937 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7940 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7941 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7942 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7943 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7944 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7945 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7946 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7948 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7949 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7950 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7951 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7955 /*---------------------------------------------------------------------------*\
\r
7957 * Front-end interface functions exported by XBoard.
\r
7958 * Functions appear in same order as prototypes in frontend.h.
\r
7960 \*---------------------------------------------------------------------------*/
\r
7962 CheckMark(UINT item, int state)
\r
7964 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7970 static UINT prevChecked = 0;
\r
7971 static int prevPausing = 0;
\r
7974 if (pausing != prevPausing) {
\r
7975 prevPausing = pausing;
\r
7976 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7977 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7978 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7981 switch (gameMode) {
\r
7982 case BeginningOfGame:
\r
7983 if (appData.icsActive)
\r
7984 nowChecked = IDM_IcsClient;
\r
7985 else if (appData.noChessProgram)
\r
7986 nowChecked = IDM_EditGame;
\r
7988 nowChecked = IDM_MachineBlack;
\r
7990 case MachinePlaysBlack:
\r
7991 nowChecked = IDM_MachineBlack;
\r
7993 case MachinePlaysWhite:
\r
7994 nowChecked = IDM_MachineWhite;
\r
7996 case TwoMachinesPlay:
\r
7997 nowChecked = IDM_TwoMachines;
\r
8000 nowChecked = IDM_AnalysisMode;
\r
8003 nowChecked = IDM_AnalyzeFile;
\r
8006 nowChecked = IDM_EditGame;
\r
8008 case PlayFromGameFile:
\r
8009 nowChecked = IDM_LoadGame;
\r
8011 case EditPosition:
\r
8012 nowChecked = IDM_EditPosition;
\r
8015 nowChecked = IDM_Training;
\r
8017 case IcsPlayingWhite:
\r
8018 case IcsPlayingBlack:
\r
8019 case IcsObserving:
\r
8021 nowChecked = IDM_IcsClient;
\r
8028 CheckMark(prevChecked, MF_UNCHECKED);
\r
8029 CheckMark(nowChecked, MF_CHECKED);
\r
8030 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8032 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8033 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8034 MF_BYCOMMAND|MF_ENABLED);
\r
8036 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8037 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8040 prevChecked = nowChecked;
\r
8042 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8043 if (appData.icsActive) {
\r
8044 if (appData.icsEngineAnalyze) {
\r
8045 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8047 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8050 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8056 HMENU hmenu = GetMenu(hwndMain);
\r
8057 SetMenuEnables(hmenu, icsEnables);
\r
8058 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8059 MF_BYCOMMAND|MF_ENABLED);
\r
8061 if (appData.zippyPlay) {
\r
8062 SetMenuEnables(hmenu, zippyEnables);
\r
8063 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8064 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8065 MF_BYCOMMAND|MF_ENABLED);
\r
8073 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8079 HMENU hmenu = GetMenu(hwndMain);
\r
8080 SetMenuEnables(hmenu, ncpEnables);
\r
8081 DrawMenuBar(hwndMain);
\r
8087 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8091 SetTrainingModeOn()
\r
8094 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8095 for (i = 0; i < N_BUTTONS; i++) {
\r
8096 if (buttonDesc[i].hwnd != NULL)
\r
8097 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8102 VOID SetTrainingModeOff()
\r
8105 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8106 for (i = 0; i < N_BUTTONS; i++) {
\r
8107 if (buttonDesc[i].hwnd != NULL)
\r
8108 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8114 SetUserThinkingEnables()
\r
8116 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8120 SetMachineThinkingEnables()
\r
8122 HMENU hMenu = GetMenu(hwndMain);
\r
8123 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8125 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8127 if (gameMode == MachinePlaysBlack) {
\r
8128 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8129 } else if (gameMode == MachinePlaysWhite) {
\r
8130 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8131 } else if (gameMode == TwoMachinesPlay) {
\r
8132 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8138 DisplayTitle(char *str)
\r
8140 char title[MSG_SIZ], *host;
\r
8141 if (str[0] != NULLCHAR) {
\r
8142 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8143 } else if (appData.icsActive) {
\r
8144 if (appData.icsCommPort[0] != NULLCHAR)
\r
8147 host = appData.icsHost;
\r
8148 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8149 } else if (appData.noChessProgram) {
\r
8150 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8152 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8153 strcat(title, ": ");
\r
8154 strcat(title, first.tidy);
\r
8156 SetWindowText(hwndMain, title);
\r
8161 DisplayMessage(char *str1, char *str2)
\r
8165 int remain = MESSAGE_TEXT_MAX - 1;
\r
8168 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8169 messageText[0] = NULLCHAR;
\r
8171 len = strlen(str1);
\r
8172 if (len > remain) len = remain;
\r
8173 strncpy(messageText, str1, len);
\r
8174 messageText[len] = NULLCHAR;
\r
8177 if (*str2 && remain >= 2) {
\r
8179 strcat(messageText, " ");
\r
8182 len = strlen(str2);
\r
8183 if (len > remain) len = remain;
\r
8184 strncat(messageText, str2, len);
\r
8186 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8187 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8189 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8193 hdc = GetDC(hwndMain);
\r
8194 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8195 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8196 &messageRect, messageText, strlen(messageText), NULL);
\r
8197 (void) SelectObject(hdc, oldFont);
\r
8198 (void) ReleaseDC(hwndMain, hdc);
\r
8202 DisplayError(char *str, int error)
\r
8204 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8208 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8210 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8211 NULL, error, LANG_NEUTRAL,
\r
8212 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8214 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8216 ErrorMap *em = errmap;
\r
8217 while (em->err != 0 && em->err != error) em++;
\r
8218 if (em->err != 0) {
\r
8219 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8221 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8226 ErrorPopUp(_("Error"), buf);
\r
8231 DisplayMoveError(char *str)
\r
8233 fromX = fromY = -1;
\r
8234 ClearHighlights();
\r
8235 DrawPosition(FALSE, NULL);
\r
8236 if (appData.popupMoveErrors) {
\r
8237 ErrorPopUp(_("Error"), str);
\r
8239 DisplayMessage(str, "");
\r
8240 moveErrorMessageUp = TRUE;
\r
8245 DisplayFatalError(char *str, int error, int exitStatus)
\r
8247 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8249 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8252 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8253 NULL, error, LANG_NEUTRAL,
\r
8254 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8256 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8258 ErrorMap *em = errmap;
\r
8259 while (em->err != 0 && em->err != error) em++;
\r
8260 if (em->err != 0) {
\r
8261 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8263 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8268 if (appData.debugMode) {
\r
8269 fprintf(debugFP, "%s: %s\n", label, str);
\r
8271 if (appData.popupExitMessage) {
\r
8272 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8273 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8275 ExitEvent(exitStatus);
\r
8280 DisplayInformation(char *str)
\r
8282 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8287 DisplayNote(char *str)
\r
8289 ErrorPopUp(_("Note"), str);
\r
8294 char *title, *question, *replyPrefix;
\r
8299 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8301 static QuestionParams *qp;
\r
8302 char reply[MSG_SIZ];
\r
8305 switch (message) {
\r
8306 case WM_INITDIALOG:
\r
8307 qp = (QuestionParams *) lParam;
\r
8308 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8309 Translate(hDlg, DLG_Question);
\r
8310 SetWindowText(hDlg, qp->title);
\r
8311 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8312 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8316 switch (LOWORD(wParam)) {
\r
8318 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8319 if (*reply) strcat(reply, " ");
\r
8320 len = strlen(reply);
\r
8321 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8322 strcat(reply, "\n");
\r
8323 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8324 EndDialog(hDlg, TRUE);
\r
8325 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8328 EndDialog(hDlg, FALSE);
\r
8339 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8341 QuestionParams qp;
\r
8345 qp.question = question;
\r
8346 qp.replyPrefix = replyPrefix;
\r
8348 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8349 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8350 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8351 FreeProcInstance(lpProc);
\r
8354 /* [AS] Pick FRC position */
\r
8355 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8357 static int * lpIndexFRC;
\r
8363 case WM_INITDIALOG:
\r
8364 lpIndexFRC = (int *) lParam;
\r
8366 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8367 Translate(hDlg, DLG_NewGameFRC);
\r
8369 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8370 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8371 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8372 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8377 switch( LOWORD(wParam) ) {
\r
8379 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8380 EndDialog( hDlg, 0 );
\r
8381 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8384 EndDialog( hDlg, 1 );
\r
8386 case IDC_NFG_Edit:
\r
8387 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8388 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8390 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8393 case IDC_NFG_Random:
\r
8394 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8395 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8408 int index = appData.defaultFrcPosition;
\r
8409 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8411 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8413 if( result == 0 ) {
\r
8414 appData.defaultFrcPosition = index;
\r
8420 /* [AS] Game list options. Refactored by HGM */
\r
8422 HWND gameListOptionsDialog;
\r
8424 // low-level front-end: clear text edit / list widget
\r
8428 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8431 // low-level front-end: clear text edit / list widget
\r
8433 GLT_DeSelectList()
\r
8435 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8438 // low-level front-end: append line to text edit / list widget
\r
8440 GLT_AddToList( char *name )
\r
8443 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8447 // low-level front-end: get line from text edit / list widget
\r
8449 GLT_GetFromList( int index, char *name )
\r
8452 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8458 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8460 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8461 int idx2 = idx1 + delta;
\r
8462 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8464 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8467 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8468 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8469 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8470 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8474 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8478 case WM_INITDIALOG:
\r
8479 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8481 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8482 Translate(hDlg, DLG_GameListOptions);
\r
8484 /* Initialize list */
\r
8485 GLT_TagsToList( lpUserGLT );
\r
8487 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8492 switch( LOWORD(wParam) ) {
\r
8495 EndDialog( hDlg, 0 );
\r
8498 EndDialog( hDlg, 1 );
\r
8501 case IDC_GLT_Default:
\r
8502 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8505 case IDC_GLT_Restore:
\r
8506 GLT_TagsToList( appData.gameListTags );
\r
8510 GLT_MoveSelection( hDlg, -1 );
\r
8513 case IDC_GLT_Down:
\r
8514 GLT_MoveSelection( hDlg, +1 );
\r
8524 int GameListOptions()
\r
8527 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8529 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8531 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8533 if( result == 0 ) {
\r
8534 /* [AS] Memory leak here! */
\r
8535 appData.gameListTags = strdup( lpUserGLT );
\r
8542 DisplayIcsInteractionTitle(char *str)
\r
8544 char consoleTitle[MSG_SIZ];
\r
8546 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8547 SetWindowText(hwndConsole, consoleTitle);
\r
8549 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8550 char buf[MSG_SIZ], *p = buf, *q;
\r
8551 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8553 q = strchr(p, ';');
\r
8555 if(*p) ChatPopUp(p);
\r
8559 SetActiveWindow(hwndMain);
\r
8563 DrawPosition(int fullRedraw, Board board)
\r
8565 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8568 void NotifyFrontendLogin()
\r
8571 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8577 fromX = fromY = -1;
\r
8578 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8579 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8580 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8581 dragInfo.lastpos = dragInfo.pos;
\r
8582 dragInfo.start.x = dragInfo.start.y = -1;
\r
8583 dragInfo.from = dragInfo.start;
\r
8585 DrawPosition(TRUE, NULL);
\r
8592 CommentPopUp(char *title, char *str)
\r
8594 HWND hwnd = GetActiveWindow();
\r
8595 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8597 SetActiveWindow(hwnd);
\r
8601 CommentPopDown(void)
\r
8603 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8604 if (commentDialog) {
\r
8605 ShowWindow(commentDialog, SW_HIDE);
\r
8607 commentUp = FALSE;
\r
8611 EditCommentPopUp(int index, char *title, char *str)
\r
8613 EitherCommentPopUp(index, title, str, TRUE);
\r
8620 MyPlaySound(&sounds[(int)SoundMove]);
\r
8623 VOID PlayIcsWinSound()
\r
8625 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8628 VOID PlayIcsLossSound()
\r
8630 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8633 VOID PlayIcsDrawSound()
\r
8635 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8638 VOID PlayIcsUnfinishedSound()
\r
8640 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8646 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8652 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8660 consoleEcho = TRUE;
\r
8661 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8662 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8663 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8672 consoleEcho = FALSE;
\r
8673 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8674 /* This works OK: set text and background both to the same color */
\r
8676 cf.crTextColor = COLOR_ECHOOFF;
\r
8677 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8678 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8681 /* No Raw()...? */
\r
8683 void Colorize(ColorClass cc, int continuation)
\r
8685 currentColorClass = cc;
\r
8686 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8687 consoleCF.crTextColor = textAttribs[cc].color;
\r
8688 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8689 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8695 static char buf[MSG_SIZ];
\r
8696 DWORD bufsiz = MSG_SIZ;
\r
8698 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8699 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8701 if (!GetUserName(buf, &bufsiz)) {
\r
8702 /*DisplayError("Error getting user name", GetLastError());*/
\r
8703 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8711 static char buf[MSG_SIZ];
\r
8712 DWORD bufsiz = MSG_SIZ;
\r
8714 if (!GetComputerName(buf, &bufsiz)) {
\r
8715 /*DisplayError("Error getting host name", GetLastError());*/
\r
8716 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8723 ClockTimerRunning()
\r
8725 return clockTimerEvent != 0;
\r
8731 if (clockTimerEvent == 0) return FALSE;
\r
8732 KillTimer(hwndMain, clockTimerEvent);
\r
8733 clockTimerEvent = 0;
\r
8738 StartClockTimer(long millisec)
\r
8740 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8741 (UINT) millisec, NULL);
\r
8745 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8748 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8750 if(appData.noGUI) return;
\r
8751 hdc = GetDC(hwndMain);
\r
8752 if (!IsIconic(hwndMain)) {
\r
8753 DisplayAClock(hdc, timeRemaining, highlight,
\r
8754 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8756 if (highlight && iconCurrent == iconBlack) {
\r
8757 iconCurrent = iconWhite;
\r
8758 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8759 if (IsIconic(hwndMain)) {
\r
8760 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8763 (void) ReleaseDC(hwndMain, hdc);
\r
8765 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8769 DisplayBlackClock(long timeRemaining, int highlight)
\r
8772 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8774 if(appData.noGUI) return;
\r
8775 hdc = GetDC(hwndMain);
\r
8776 if (!IsIconic(hwndMain)) {
\r
8777 DisplayAClock(hdc, timeRemaining, highlight,
\r
8778 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8780 if (highlight && iconCurrent == iconWhite) {
\r
8781 iconCurrent = iconBlack;
\r
8782 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8783 if (IsIconic(hwndMain)) {
\r
8784 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8787 (void) ReleaseDC(hwndMain, hdc);
\r
8789 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8794 LoadGameTimerRunning()
\r
8796 return loadGameTimerEvent != 0;
\r
8800 StopLoadGameTimer()
\r
8802 if (loadGameTimerEvent == 0) return FALSE;
\r
8803 KillTimer(hwndMain, loadGameTimerEvent);
\r
8804 loadGameTimerEvent = 0;
\r
8809 StartLoadGameTimer(long millisec)
\r
8811 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8812 (UINT) millisec, NULL);
\r
8820 char fileTitle[MSG_SIZ];
\r
8822 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8823 f = OpenFileDialog(hwndMain, "a", defName,
\r
8824 appData.oldSaveStyle ? "gam" : "pgn",
\r
8826 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8828 SaveGame(f, 0, "");
\r
8835 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8837 if (delayedTimerEvent != 0) {
\r
8838 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8839 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8841 KillTimer(hwndMain, delayedTimerEvent);
\r
8842 delayedTimerEvent = 0;
\r
8843 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8844 delayedTimerCallback();
\r
8846 delayedTimerCallback = cb;
\r
8847 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8848 (UINT) millisec, NULL);
\r
8851 DelayedEventCallback
\r
8854 if (delayedTimerEvent) {
\r
8855 return delayedTimerCallback;
\r
8862 CancelDelayedEvent()
\r
8864 if (delayedTimerEvent) {
\r
8865 KillTimer(hwndMain, delayedTimerEvent);
\r
8866 delayedTimerEvent = 0;
\r
8870 DWORD GetWin32Priority(int nice)
\r
8871 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8873 REALTIME_PRIORITY_CLASS 0x00000100
\r
8874 HIGH_PRIORITY_CLASS 0x00000080
\r
8875 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8876 NORMAL_PRIORITY_CLASS 0x00000020
\r
8877 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8878 IDLE_PRIORITY_CLASS 0x00000040
\r
8880 if (nice < -15) return 0x00000080;
\r
8881 if (nice < 0) return 0x00008000;
\r
8882 if (nice == 0) return 0x00000020;
\r
8883 if (nice < 15) return 0x00004000;
\r
8884 return 0x00000040;
\r
8887 void RunCommand(char *cmdLine)
\r
8889 /* Now create the child process. */
\r
8890 STARTUPINFO siStartInfo;
\r
8891 PROCESS_INFORMATION piProcInfo;
\r
8893 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8894 siStartInfo.lpReserved = NULL;
\r
8895 siStartInfo.lpDesktop = NULL;
\r
8896 siStartInfo.lpTitle = NULL;
\r
8897 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8898 siStartInfo.cbReserved2 = 0;
\r
8899 siStartInfo.lpReserved2 = NULL;
\r
8900 siStartInfo.hStdInput = NULL;
\r
8901 siStartInfo.hStdOutput = NULL;
\r
8902 siStartInfo.hStdError = NULL;
\r
8904 CreateProcess(NULL,
\r
8905 cmdLine, /* command line */
\r
8906 NULL, /* process security attributes */
\r
8907 NULL, /* primary thread security attrs */
\r
8908 TRUE, /* handles are inherited */
\r
8909 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8910 NULL, /* use parent's environment */
\r
8912 &siStartInfo, /* STARTUPINFO pointer */
\r
8913 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8915 CloseHandle(piProcInfo.hThread);
\r
8918 /* Start a child process running the given program.
\r
8919 The process's standard output can be read from "from", and its
\r
8920 standard input can be written to "to".
\r
8921 Exit with fatal error if anything goes wrong.
\r
8922 Returns an opaque pointer that can be used to destroy the process
\r
8926 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8928 #define BUFSIZE 4096
\r
8930 HANDLE hChildStdinRd, hChildStdinWr,
\r
8931 hChildStdoutRd, hChildStdoutWr;
\r
8932 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8933 SECURITY_ATTRIBUTES saAttr;
\r
8935 PROCESS_INFORMATION piProcInfo;
\r
8936 STARTUPINFO siStartInfo;
\r
8938 char buf[MSG_SIZ];
\r
8941 if (appData.debugMode) {
\r
8942 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8947 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8948 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8949 saAttr.bInheritHandle = TRUE;
\r
8950 saAttr.lpSecurityDescriptor = NULL;
\r
8953 * The steps for redirecting child's STDOUT:
\r
8954 * 1. Create anonymous pipe to be STDOUT for child.
\r
8955 * 2. Create a noninheritable duplicate of read handle,
\r
8956 * and close the inheritable read handle.
\r
8959 /* Create a pipe for the child's STDOUT. */
\r
8960 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8961 return GetLastError();
\r
8964 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8965 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8966 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8967 FALSE, /* not inherited */
\r
8968 DUPLICATE_SAME_ACCESS);
\r
8970 return GetLastError();
\r
8972 CloseHandle(hChildStdoutRd);
\r
8975 * The steps for redirecting child's STDIN:
\r
8976 * 1. Create anonymous pipe to be STDIN for child.
\r
8977 * 2. Create a noninheritable duplicate of write handle,
\r
8978 * and close the inheritable write handle.
\r
8981 /* Create a pipe for the child's STDIN. */
\r
8982 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8983 return GetLastError();
\r
8986 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8987 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8988 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8989 FALSE, /* not inherited */
\r
8990 DUPLICATE_SAME_ACCESS);
\r
8992 return GetLastError();
\r
8994 CloseHandle(hChildStdinWr);
\r
8996 /* Arrange to (1) look in dir for the child .exe file, and
\r
8997 * (2) have dir be the child's working directory. Interpret
\r
8998 * dir relative to the directory WinBoard loaded from. */
\r
8999 GetCurrentDirectory(MSG_SIZ, buf);
\r
9000 SetCurrentDirectory(installDir);
\r
9001 SetCurrentDirectory(dir);
\r
9003 /* Now create the child process. */
\r
9005 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9006 siStartInfo.lpReserved = NULL;
\r
9007 siStartInfo.lpDesktop = NULL;
\r
9008 siStartInfo.lpTitle = NULL;
\r
9009 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9010 siStartInfo.cbReserved2 = 0;
\r
9011 siStartInfo.lpReserved2 = NULL;
\r
9012 siStartInfo.hStdInput = hChildStdinRd;
\r
9013 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9014 siStartInfo.hStdError = hChildStdoutWr;
\r
9016 fSuccess = CreateProcess(NULL,
\r
9017 cmdLine, /* command line */
\r
9018 NULL, /* process security attributes */
\r
9019 NULL, /* primary thread security attrs */
\r
9020 TRUE, /* handles are inherited */
\r
9021 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9022 NULL, /* use parent's environment */
\r
9024 &siStartInfo, /* STARTUPINFO pointer */
\r
9025 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9027 err = GetLastError();
\r
9028 SetCurrentDirectory(buf); /* return to prev directory */
\r
9033 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9034 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9035 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9038 /* Close the handles we don't need in the parent */
\r
9039 CloseHandle(piProcInfo.hThread);
\r
9040 CloseHandle(hChildStdinRd);
\r
9041 CloseHandle(hChildStdoutWr);
\r
9043 /* Prepare return value */
\r
9044 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9045 cp->kind = CPReal;
\r
9046 cp->hProcess = piProcInfo.hProcess;
\r
9047 cp->pid = piProcInfo.dwProcessId;
\r
9048 cp->hFrom = hChildStdoutRdDup;
\r
9049 cp->hTo = hChildStdinWrDup;
\r
9051 *pr = (void *) cp;
\r
9053 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9054 2000 where engines sometimes don't see the initial command(s)
\r
9055 from WinBoard and hang. I don't understand how that can happen,
\r
9056 but the Sleep is harmless, so I've put it in. Others have also
\r
9057 reported what may be the same problem, so hopefully this will fix
\r
9058 it for them too. */
\r
9066 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9068 ChildProc *cp; int result;
\r
9070 cp = (ChildProc *) pr;
\r
9071 if (cp == NULL) return;
\r
9073 switch (cp->kind) {
\r
9075 /* TerminateProcess is considered harmful, so... */
\r
9076 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9077 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9078 /* The following doesn't work because the chess program
\r
9079 doesn't "have the same console" as WinBoard. Maybe
\r
9080 we could arrange for this even though neither WinBoard
\r
9081 nor the chess program uses a console for stdio? */
\r
9082 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9084 /* [AS] Special termination modes for misbehaving programs... */
\r
9085 if( signal == 9 ) {
\r
9086 result = TerminateProcess( cp->hProcess, 0 );
\r
9088 if ( appData.debugMode) {
\r
9089 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9092 else if( signal == 10 ) {
\r
9093 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9095 if( dw != WAIT_OBJECT_0 ) {
\r
9096 result = TerminateProcess( cp->hProcess, 0 );
\r
9098 if ( appData.debugMode) {
\r
9099 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9105 CloseHandle(cp->hProcess);
\r
9109 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9113 closesocket(cp->sock);
\r
9118 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9119 closesocket(cp->sock);
\r
9120 closesocket(cp->sock2);
\r
9128 InterruptChildProcess(ProcRef pr)
\r
9132 cp = (ChildProc *) pr;
\r
9133 if (cp == NULL) return;
\r
9134 switch (cp->kind) {
\r
9136 /* The following doesn't work because the chess program
\r
9137 doesn't "have the same console" as WinBoard. Maybe
\r
9138 we could arrange for this even though neither WinBoard
\r
9139 nor the chess program uses a console for stdio */
\r
9140 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9145 /* Can't interrupt */
\r
9149 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9156 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9158 char cmdLine[MSG_SIZ];
\r
9160 if (port[0] == NULLCHAR) {
\r
9161 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9163 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9165 return StartChildProcess(cmdLine, "", pr);
\r
9169 /* Code to open TCP sockets */
\r
9172 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9177 struct sockaddr_in sa, mysa;
\r
9178 struct hostent FAR *hp;
\r
9179 unsigned short uport;
\r
9180 WORD wVersionRequested;
\r
9183 /* Initialize socket DLL */
\r
9184 wVersionRequested = MAKEWORD(1, 1);
\r
9185 err = WSAStartup(wVersionRequested, &wsaData);
\r
9186 if (err != 0) return err;
\r
9189 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9190 err = WSAGetLastError();
\r
9195 /* Bind local address using (mostly) don't-care values.
\r
9197 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9198 mysa.sin_family = AF_INET;
\r
9199 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9200 uport = (unsigned short) 0;
\r
9201 mysa.sin_port = htons(uport);
\r
9202 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9203 == SOCKET_ERROR) {
\r
9204 err = WSAGetLastError();
\r
9209 /* Resolve remote host name */
\r
9210 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9211 if (!(hp = gethostbyname(host))) {
\r
9212 unsigned int b0, b1, b2, b3;
\r
9214 err = WSAGetLastError();
\r
9216 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9217 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9218 hp->h_addrtype = AF_INET;
\r
9220 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9221 hp->h_addr_list[0] = (char *) malloc(4);
\r
9222 hp->h_addr_list[0][0] = (char) b0;
\r
9223 hp->h_addr_list[0][1] = (char) b1;
\r
9224 hp->h_addr_list[0][2] = (char) b2;
\r
9225 hp->h_addr_list[0][3] = (char) b3;
\r
9231 sa.sin_family = hp->h_addrtype;
\r
9232 uport = (unsigned short) atoi(port);
\r
9233 sa.sin_port = htons(uport);
\r
9234 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9236 /* Make connection */
\r
9237 if (connect(s, (struct sockaddr *) &sa,
\r
9238 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9239 err = WSAGetLastError();
\r
9244 /* Prepare return value */
\r
9245 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9246 cp->kind = CPSock;
\r
9248 *pr = (ProcRef *) cp;
\r
9254 OpenCommPort(char *name, ProcRef *pr)
\r
9259 char fullname[MSG_SIZ];
\r
9261 if (*name != '\\')
\r
9262 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9264 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9266 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9267 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9268 if (h == (HANDLE) -1) {
\r
9269 return GetLastError();
\r
9273 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9275 /* Accumulate characters until a 100ms pause, then parse */
\r
9276 ct.ReadIntervalTimeout = 100;
\r
9277 ct.ReadTotalTimeoutMultiplier = 0;
\r
9278 ct.ReadTotalTimeoutConstant = 0;
\r
9279 ct.WriteTotalTimeoutMultiplier = 0;
\r
9280 ct.WriteTotalTimeoutConstant = 0;
\r
9281 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9283 /* Prepare return value */
\r
9284 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9285 cp->kind = CPComm;
\r
9288 *pr = (ProcRef *) cp;
\r
9294 OpenLoopback(ProcRef *pr)
\r
9296 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9302 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9307 struct sockaddr_in sa, mysa;
\r
9308 struct hostent FAR *hp;
\r
9309 unsigned short uport;
\r
9310 WORD wVersionRequested;
\r
9313 char stderrPortStr[MSG_SIZ];
\r
9315 /* Initialize socket DLL */
\r
9316 wVersionRequested = MAKEWORD(1, 1);
\r
9317 err = WSAStartup(wVersionRequested, &wsaData);
\r
9318 if (err != 0) return err;
\r
9320 /* Resolve remote host name */
\r
9321 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9322 if (!(hp = gethostbyname(host))) {
\r
9323 unsigned int b0, b1, b2, b3;
\r
9325 err = WSAGetLastError();
\r
9327 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9328 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9329 hp->h_addrtype = AF_INET;
\r
9331 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9332 hp->h_addr_list[0] = (char *) malloc(4);
\r
9333 hp->h_addr_list[0][0] = (char) b0;
\r
9334 hp->h_addr_list[0][1] = (char) b1;
\r
9335 hp->h_addr_list[0][2] = (char) b2;
\r
9336 hp->h_addr_list[0][3] = (char) b3;
\r
9342 sa.sin_family = hp->h_addrtype;
\r
9343 uport = (unsigned short) 514;
\r
9344 sa.sin_port = htons(uport);
\r
9345 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9347 /* Bind local socket to unused "privileged" port address
\r
9349 s = INVALID_SOCKET;
\r
9350 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9351 mysa.sin_family = AF_INET;
\r
9352 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9353 for (fromPort = 1023;; fromPort--) {
\r
9354 if (fromPort < 0) {
\r
9356 return WSAEADDRINUSE;
\r
9358 if (s == INVALID_SOCKET) {
\r
9359 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9360 err = WSAGetLastError();
\r
9365 uport = (unsigned short) fromPort;
\r
9366 mysa.sin_port = htons(uport);
\r
9367 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9368 == SOCKET_ERROR) {
\r
9369 err = WSAGetLastError();
\r
9370 if (err == WSAEADDRINUSE) continue;
\r
9374 if (connect(s, (struct sockaddr *) &sa,
\r
9375 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9376 err = WSAGetLastError();
\r
9377 if (err == WSAEADDRINUSE) {
\r
9388 /* Bind stderr local socket to unused "privileged" port address
\r
9390 s2 = INVALID_SOCKET;
\r
9391 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9392 mysa.sin_family = AF_INET;
\r
9393 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9394 for (fromPort = 1023;; fromPort--) {
\r
9395 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9396 if (fromPort < 0) {
\r
9397 (void) closesocket(s);
\r
9399 return WSAEADDRINUSE;
\r
9401 if (s2 == INVALID_SOCKET) {
\r
9402 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9403 err = WSAGetLastError();
\r
9409 uport = (unsigned short) fromPort;
\r
9410 mysa.sin_port = htons(uport);
\r
9411 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9412 == SOCKET_ERROR) {
\r
9413 err = WSAGetLastError();
\r
9414 if (err == WSAEADDRINUSE) continue;
\r
9415 (void) closesocket(s);
\r
9419 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9420 err = WSAGetLastError();
\r
9421 if (err == WSAEADDRINUSE) {
\r
9423 s2 = INVALID_SOCKET;
\r
9426 (void) closesocket(s);
\r
9427 (void) closesocket(s2);
\r
9433 prevStderrPort = fromPort; // remember port used
\r
9434 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9436 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9437 err = WSAGetLastError();
\r
9438 (void) closesocket(s);
\r
9439 (void) closesocket(s2);
\r
9444 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9445 err = WSAGetLastError();
\r
9446 (void) closesocket(s);
\r
9447 (void) closesocket(s2);
\r
9451 if (*user == NULLCHAR) user = UserName();
\r
9452 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9453 err = WSAGetLastError();
\r
9454 (void) closesocket(s);
\r
9455 (void) closesocket(s2);
\r
9459 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9460 err = WSAGetLastError();
\r
9461 (void) closesocket(s);
\r
9462 (void) closesocket(s2);
\r
9467 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9468 err = WSAGetLastError();
\r
9469 (void) closesocket(s);
\r
9470 (void) closesocket(s2);
\r
9474 (void) closesocket(s2); /* Stop listening */
\r
9476 /* Prepare return value */
\r
9477 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9478 cp->kind = CPRcmd;
\r
9481 *pr = (ProcRef *) cp;
\r
9488 AddInputSource(ProcRef pr, int lineByLine,
\r
9489 InputCallback func, VOIDSTAR closure)
\r
9491 InputSource *is, *is2 = NULL;
\r
9492 ChildProc *cp = (ChildProc *) pr;
\r
9494 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9495 is->lineByLine = lineByLine;
\r
9497 is->closure = closure;
\r
9498 is->second = NULL;
\r
9499 is->next = is->buf;
\r
9500 if (pr == NoProc) {
\r
9501 is->kind = CPReal;
\r
9502 consoleInputSource = is;
\r
9504 is->kind = cp->kind;
\r
9506 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9507 we create all threads suspended so that the is->hThread variable can be
\r
9508 safely assigned, then let the threads start with ResumeThread.
\r
9510 switch (cp->kind) {
\r
9512 is->hFile = cp->hFrom;
\r
9513 cp->hFrom = NULL; /* now owned by InputThread */
\r
9515 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9516 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9520 is->hFile = cp->hFrom;
\r
9521 cp->hFrom = NULL; /* now owned by InputThread */
\r
9523 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9524 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9528 is->sock = cp->sock;
\r
9530 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9531 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9535 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9537 is->sock = cp->sock;
\r
9539 is2->sock = cp->sock2;
\r
9540 is2->second = is2;
\r
9542 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9543 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9545 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9546 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9550 if( is->hThread != NULL ) {
\r
9551 ResumeThread( is->hThread );
\r
9554 if( is2 != NULL && is2->hThread != NULL ) {
\r
9555 ResumeThread( is2->hThread );
\r
9559 return (InputSourceRef) is;
\r
9563 RemoveInputSource(InputSourceRef isr)
\r
9567 is = (InputSource *) isr;
\r
9568 is->hThread = NULL; /* tell thread to stop */
\r
9569 CloseHandle(is->hThread);
\r
9570 if (is->second != NULL) {
\r
9571 is->second->hThread = NULL;
\r
9572 CloseHandle(is->second->hThread);
\r
9576 int no_wrap(char *message, int count)
\r
9578 ConsoleOutput(message, count, FALSE);
\r
9583 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9586 int outCount = SOCKET_ERROR;
\r
9587 ChildProc *cp = (ChildProc *) pr;
\r
9588 static OVERLAPPED ovl;
\r
9589 static int line = 0;
\r
9593 if (appData.noJoin || !appData.useInternalWrap)
\r
9594 return no_wrap(message, count);
\r
9597 int width = get_term_width();
\r
9598 int len = wrap(NULL, message, count, width, &line);
\r
9599 char *msg = malloc(len);
\r
9603 return no_wrap(message, count);
\r
9606 dbgchk = wrap(msg, message, count, width, &line);
\r
9607 if (dbgchk != len && appData.debugMode)
\r
9608 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9609 ConsoleOutput(msg, len, FALSE);
\r
9616 if (ovl.hEvent == NULL) {
\r
9617 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9619 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9621 switch (cp->kind) {
\r
9624 outCount = send(cp->sock, message, count, 0);
\r
9625 if (outCount == SOCKET_ERROR) {
\r
9626 *outError = WSAGetLastError();
\r
9628 *outError = NO_ERROR;
\r
9633 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9634 &dOutCount, NULL)) {
\r
9635 *outError = NO_ERROR;
\r
9636 outCount = (int) dOutCount;
\r
9638 *outError = GetLastError();
\r
9643 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9644 &dOutCount, &ovl);
\r
9645 if (*outError == NO_ERROR) {
\r
9646 outCount = (int) dOutCount;
\r
9656 if(n != 0) Sleep(n);
\r
9660 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9663 /* Ignore delay, not implemented for WinBoard */
\r
9664 return OutputToProcess(pr, message, count, outError);
\r
9669 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9670 char *buf, int count, int error)
\r
9672 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9675 /* see wgamelist.c for Game List functions */
\r
9676 /* see wedittags.c for Edit Tags functions */
\r
9683 char buf[MSG_SIZ];
\r
9686 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9687 f = fopen(buf, "r");
\r
9689 ProcessICSInitScript(f);
\r
9697 StartAnalysisClock()
\r
9699 if (analysisTimerEvent) return;
\r
9700 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9701 (UINT) 2000, NULL);
\r
9705 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9707 highlightInfo.sq[0].x = fromX;
\r
9708 highlightInfo.sq[0].y = fromY;
\r
9709 highlightInfo.sq[1].x = toX;
\r
9710 highlightInfo.sq[1].y = toY;
\r
9716 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9717 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9721 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9723 premoveHighlightInfo.sq[0].x = fromX;
\r
9724 premoveHighlightInfo.sq[0].y = fromY;
\r
9725 premoveHighlightInfo.sq[1].x = toX;
\r
9726 premoveHighlightInfo.sq[1].y = toY;
\r
9730 ClearPremoveHighlights()
\r
9732 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9733 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9737 ShutDownFrontEnd()
\r
9739 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9740 DeleteClipboardTempFiles();
\r
9746 if (IsIconic(hwndMain))
\r
9747 ShowWindow(hwndMain, SW_RESTORE);
\r
9749 SetActiveWindow(hwndMain);
\r
9753 * Prototypes for animation support routines
\r
9755 static void ScreenSquare(int column, int row, POINT * pt);
\r
9756 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9757 POINT frames[], int * nFrames);
\r
9763 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9764 { // [HGM] atomic: animate blast wave
\r
9767 explodeInfo.fromX = fromX;
\r
9768 explodeInfo.fromY = fromY;
\r
9769 explodeInfo.toX = toX;
\r
9770 explodeInfo.toY = toY;
\r
9771 for(i=1; i<4*kFactor; i++) {
\r
9772 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9773 DrawPosition(FALSE, board);
\r
9774 Sleep(appData.animSpeed);
\r
9776 explodeInfo.radius = 0;
\r
9777 DrawPosition(TRUE, board);
\r
9781 AnimateMove(board, fromX, fromY, toX, toY)
\r
9788 ChessSquare piece;
\r
9789 POINT start, finish, mid;
\r
9790 POINT frames[kFactor * 2 + 1];
\r
9793 if (!appData.animate) return;
\r
9794 if (doingSizing) return;
\r
9795 if (fromY < 0 || fromX < 0) return;
\r
9796 piece = board[fromY][fromX];
\r
9797 if (piece >= EmptySquare) return;
\r
9799 ScreenSquare(fromX, fromY, &start);
\r
9800 ScreenSquare(toX, toY, &finish);
\r
9802 /* All moves except knight jumps move in straight line */
\r
9803 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9804 mid.x = start.x + (finish.x - start.x) / 2;
\r
9805 mid.y = start.y + (finish.y - start.y) / 2;
\r
9807 /* Knight: make straight movement then diagonal */
\r
9808 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9809 mid.x = start.x + (finish.x - start.x) / 2;
\r
9813 mid.y = start.y + (finish.y - start.y) / 2;
\r
9817 /* Don't use as many frames for very short moves */
\r
9818 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9819 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9821 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9823 animInfo.from.x = fromX;
\r
9824 animInfo.from.y = fromY;
\r
9825 animInfo.to.x = toX;
\r
9826 animInfo.to.y = toY;
\r
9827 animInfo.lastpos = start;
\r
9828 animInfo.piece = piece;
\r
9829 for (n = 0; n < nFrames; n++) {
\r
9830 animInfo.pos = frames[n];
\r
9831 DrawPosition(FALSE, NULL);
\r
9832 animInfo.lastpos = animInfo.pos;
\r
9833 Sleep(appData.animSpeed);
\r
9835 animInfo.pos = finish;
\r
9836 DrawPosition(FALSE, NULL);
\r
9837 animInfo.piece = EmptySquare;
\r
9838 Explode(board, fromX, fromY, toX, toY);
\r
9841 /* Convert board position to corner of screen rect and color */
\r
9844 ScreenSquare(column, row, pt)
\r
9845 int column; int row; POINT * pt;
\r
9848 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9849 pt->y = lineGap + row * (squareSize + lineGap);
\r
9851 pt->x = lineGap + column * (squareSize + lineGap);
\r
9852 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9856 /* Generate a series of frame coords from start->mid->finish.
\r
9857 The movement rate doubles until the half way point is
\r
9858 reached, then halves back down to the final destination,
\r
9859 which gives a nice slow in/out effect. The algorithmn
\r
9860 may seem to generate too many intermediates for short
\r
9861 moves, but remember that the purpose is to attract the
\r
9862 viewers attention to the piece about to be moved and
\r
9863 then to where it ends up. Too few frames would be less
\r
9867 Tween(start, mid, finish, factor, frames, nFrames)
\r
9868 POINT * start; POINT * mid;
\r
9869 POINT * finish; int factor;
\r
9870 POINT frames[]; int * nFrames;
\r
9872 int n, fraction = 1, count = 0;
\r
9874 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9875 for (n = 0; n < factor; n++)
\r
9877 for (n = 0; n < factor; n++) {
\r
9878 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9879 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9881 fraction = fraction / 2;
\r
9885 frames[count] = *mid;
\r
9888 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9890 for (n = 0; n < factor; n++) {
\r
9891 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9892 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9894 fraction = fraction * 2;
\r
9900 SettingsPopUp(ChessProgramState *cps)
\r
9901 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9902 EngineOptionsPopup(savedHwnd, cps);
\r
9905 int flock(int fid, int code)
\r
9907 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9911 ov.OffsetHigh = 0;
\r
9913 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9914 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9915 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9916 default: return -1;
\r