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 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 },
\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_KB_NAVIGATION
\r
741 #define JAWS_MENU_ITEMS
\r
742 #define JAWS_SILENCE
\r
743 #define JAWS_REPLAY
\r
745 #define JAWS_COPYRIGHT
\r
746 #define JAWS_DELETE(X) X
\r
747 #define SAYMACHINEMOVE()
\r
751 /*---------------------------------------------------------------------------*\
\r
755 \*---------------------------------------------------------------------------*/
\r
758 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
759 LPSTR lpCmdLine, int nCmdShow)
\r
762 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
763 // INITCOMMONCONTROLSEX ex;
\r
767 LoadLibrary("RICHED32.DLL");
\r
768 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
770 if (!InitApplication(hInstance)) {
\r
773 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
780 // InitCommonControlsEx(&ex);
\r
781 InitCommonControls();
\r
783 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
784 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
785 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
787 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
789 while (GetMessage(&msg, /* message structure */
\r
790 NULL, /* handle of window receiving the message */
\r
791 0, /* lowest message to examine */
\r
792 0)) /* highest message to examine */
\r
795 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
796 // [HGM] navigate: switch between all windows with tab
\r
797 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
798 int i, currentElement = 0;
\r
800 // first determine what element of the chain we come from (if any)
\r
801 if(appData.icsActive) {
\r
802 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
803 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
805 if(engineOutputDialog && EngineOutputIsUp()) {
\r
806 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
807 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
809 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
810 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
812 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
813 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
814 if(msg.hwnd == e1) currentElement = 2; else
\r
815 if(msg.hwnd == e2) currentElement = 3; else
\r
816 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
817 if(msg.hwnd == mh) currentElement = 4; else
\r
818 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
819 if(msg.hwnd == hText) currentElement = 5; else
\r
820 if(msg.hwnd == hInput) currentElement = 6; else
\r
821 for (i = 0; i < N_BUTTONS; i++) {
\r
822 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
825 // determine where to go to
\r
826 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
828 currentElement = (currentElement + direction) % 7;
\r
829 switch(currentElement) {
\r
831 h = hwndMain; break; // passing this case always makes the loop exit
\r
833 h = buttonDesc[0].hwnd; break; // could be NULL
\r
835 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
838 if(!EngineOutputIsUp()) continue;
\r
841 if(!MoveHistoryIsUp()) continue;
\r
843 // case 6: // input to eval graph does not seem to get here!
\r
844 // if(!EvalGraphIsUp()) continue;
\r
845 // h = evalGraphDialog; break;
\r
847 if(!appData.icsActive) continue;
\r
851 if(!appData.icsActive) continue;
\r
857 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
858 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
861 continue; // this message now has been processed
\r
865 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
866 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
867 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
868 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
869 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
870 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
871 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
872 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
873 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
874 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
875 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
876 for(i=0; i<MAX_CHAT; i++)
\r
877 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
880 if(done) continue; // [HGM] chat: end patch
\r
881 TranslateMessage(&msg); /* Translates virtual key codes */
\r
882 DispatchMessage(&msg); /* Dispatches message to window */
\r
887 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
890 /*---------------------------------------------------------------------------*\
\r
892 * Initialization functions
\r
894 \*---------------------------------------------------------------------------*/
\r
898 { // update user logo if necessary
\r
899 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
901 if(appData.autoLogo) {
\r
902 curName = UserName();
\r
903 if(strcmp(curName, oldUserName)) {
\r
904 GetCurrentDirectory(MSG_SIZ, dir);
\r
905 SetCurrentDirectory(installDir);
\r
906 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
907 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
908 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
909 if(userLogo == NULL)
\r
910 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
911 SetCurrentDirectory(dir); /* return to prev directory */
\r
917 InitApplication(HINSTANCE hInstance)
\r
921 /* Fill in window class structure with parameters that describe the */
\r
924 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
925 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
926 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
927 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
928 wc.hInstance = hInstance; /* Owner of this class */
\r
929 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
930 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
931 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
932 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
933 wc.lpszClassName = szAppName; /* Name to register as */
\r
935 /* Register the window class and return success/failure code. */
\r
936 if (!RegisterClass(&wc)) return FALSE;
\r
938 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
939 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
941 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
942 wc.hInstance = hInstance;
\r
943 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
944 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
945 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
946 wc.lpszMenuName = NULL;
\r
947 wc.lpszClassName = szConsoleName;
\r
949 if (!RegisterClass(&wc)) return FALSE;
\r
954 /* Set by InitInstance, used by EnsureOnScreen */
\r
955 int screenHeight, screenWidth;
\r
958 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
960 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
961 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
962 if (*x > screenWidth - 32) *x = 0;
\r
963 if (*y > screenHeight - 32) *y = 0;
\r
964 if (*x < minX) *x = minX;
\r
965 if (*y < minY) *y = minY;
\r
969 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
971 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
972 GetCurrentDirectory(MSG_SIZ, dir);
\r
973 SetCurrentDirectory(installDir);
\r
974 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
975 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
977 if (cps->programLogo == NULL && appData.debugMode) {
\r
978 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
980 } else if(appData.autoLogo) {
\r
981 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
982 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
983 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
985 if(appData.directory[n] && appData.directory[n][0]) {
\r
986 SetCurrentDirectory(appData.directory[n]);
\r
987 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
990 SetCurrentDirectory(dir); /* return to prev directory */
\r
996 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
997 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
999 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1000 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1001 liteBackTextureMode = appData.liteBackTextureMode;
\r
1003 if (liteBackTexture == NULL && appData.debugMode) {
\r
1004 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1008 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1009 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1010 darkBackTextureMode = appData.darkBackTextureMode;
\r
1012 if (darkBackTexture == NULL && appData.debugMode) {
\r
1013 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1019 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1021 HWND hwnd; /* Main window handle. */
\r
1023 WINDOWPLACEMENT wp;
\r
1026 hInst = hInstance; /* Store instance handle in our global variable */
\r
1027 programName = szAppName;
\r
1029 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1030 *filepart = NULLCHAR;
\r
1032 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1034 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1035 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1036 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1037 /* xboard, and older WinBoards, controlled the move sound with the
\r
1038 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1039 always turn the option on (so that the backend will call us),
\r
1040 then let the user turn the sound off by setting it to silence if
\r
1041 desired. To accommodate old winboard.ini files saved by old
\r
1042 versions of WinBoard, we also turn off the sound if the option
\r
1043 was initially set to false. [HGM] taken out of InitAppData */
\r
1044 if (!appData.ringBellAfterMoves) {
\r
1045 sounds[(int)SoundMove].name = strdup("");
\r
1046 appData.ringBellAfterMoves = TRUE;
\r
1048 if (appData.debugMode) {
\r
1049 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1050 setbuf(debugFP, NULL);
\r
1053 LoadLanguageFile(appData.language);
\r
1057 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1058 // InitEngineUCI( installDir, &second );
\r
1060 /* Create a main window for this application instance. */
\r
1061 hwnd = CreateWindow(szAppName, szTitle,
\r
1062 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1063 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1064 NULL, NULL, hInstance, NULL);
\r
1067 /* If window could not be created, return "failure" */
\r
1072 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1073 LoadLogo(&first, 0, FALSE);
\r
1074 LoadLogo(&second, 1, appData.icsActive);
\r
1078 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1079 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1080 iconCurrent = iconWhite;
\r
1081 InitDrawingColors();
\r
1082 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1083 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1084 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1085 /* Compute window size for each board size, and use the largest
\r
1086 size that fits on this screen as the default. */
\r
1087 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1088 if (boardSize == (BoardSize)-1 &&
\r
1089 winH <= screenHeight
\r
1090 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1091 && winW <= screenWidth) {
\r
1092 boardSize = (BoardSize)ibs;
\r
1096 InitDrawingSizes(boardSize, 0);
\r
1098 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1100 /* [AS] Load textures if specified */
\r
1103 mysrandom( (unsigned) time(NULL) );
\r
1105 /* [AS] Restore layout */
\r
1106 if( wpMoveHistory.visible ) {
\r
1107 MoveHistoryPopUp();
\r
1110 if( wpEvalGraph.visible ) {
\r
1114 if( wpEngineOutput.visible ) {
\r
1115 EngineOutputPopUp();
\r
1118 /* Make the window visible; update its client area; and return "success" */
\r
1119 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1120 wp.length = sizeof(WINDOWPLACEMENT);
\r
1122 wp.showCmd = nCmdShow;
\r
1123 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1124 wp.rcNormalPosition.left = wpMain.x;
\r
1125 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1126 wp.rcNormalPosition.top = wpMain.y;
\r
1127 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1128 SetWindowPlacement(hwndMain, &wp);
\r
1130 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1132 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1133 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1135 if (hwndConsole) {
\r
1137 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1138 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1140 ShowWindow(hwndConsole, nCmdShow);
\r
1141 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1142 char buf[MSG_SIZ], *p = buf, *q;
\r
1143 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1145 q = strchr(p, ';');
\r
1147 if(*p) ChatPopUp(p);
\r
1150 SetActiveWindow(hwndConsole);
\r
1152 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1153 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1162 HMENU hmenu = GetMenu(hwndMain);
\r
1164 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1165 MF_BYCOMMAND|((appData.icsActive &&
\r
1166 *appData.icsCommPort != NULLCHAR) ?
\r
1167 MF_ENABLED : MF_GRAYED));
\r
1168 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1169 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1170 MF_CHECKED : MF_UNCHECKED));
\r
1173 //---------------------------------------------------------------------------------------------------------
\r
1175 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1176 #define XBOARD FALSE
\r
1178 #define OPTCHAR "/"
\r
1179 #define SEPCHAR "="
\r
1183 // front-end part of option handling
\r
1186 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1188 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1189 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1192 lf->lfEscapement = 0;
\r
1193 lf->lfOrientation = 0;
\r
1194 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1195 lf->lfItalic = mfp->italic;
\r
1196 lf->lfUnderline = mfp->underline;
\r
1197 lf->lfStrikeOut = mfp->strikeout;
\r
1198 lf->lfCharSet = mfp->charset;
\r
1199 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1200 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1201 lf->lfQuality = DEFAULT_QUALITY;
\r
1202 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1203 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1207 CreateFontInMF(MyFont *mf)
\r
1209 LFfromMFP(&mf->lf, &mf->mfp);
\r
1210 if (mf->hf) DeleteObject(mf->hf);
\r
1211 mf->hf = CreateFontIndirect(&mf->lf);
\r
1214 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1216 colorVariable[] = {
\r
1217 &whitePieceColor,
\r
1218 &blackPieceColor,
\r
1219 &lightSquareColor,
\r
1220 &darkSquareColor,
\r
1221 &highlightSquareColor,
\r
1222 &premoveHighlightColor,
\r
1224 &consoleBackgroundColor,
\r
1225 &appData.fontForeColorWhite,
\r
1226 &appData.fontBackColorWhite,
\r
1227 &appData.fontForeColorBlack,
\r
1228 &appData.fontBackColorBlack,
\r
1229 &appData.evalHistColorWhite,
\r
1230 &appData.evalHistColorBlack,
\r
1231 &appData.highlightArrowColor,
\r
1234 /* Command line font name parser. NULL name means do nothing.
\r
1235 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1236 For backward compatibility, syntax without the colon is also
\r
1237 accepted, but font names with digits in them won't work in that case.
\r
1240 ParseFontName(char *name, MyFontParams *mfp)
\r
1243 if (name == NULL) return;
\r
1245 q = strchr(p, ':');
\r
1247 if (q - p >= sizeof(mfp->faceName))
\r
1248 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1249 memcpy(mfp->faceName, p, q - p);
\r
1250 mfp->faceName[q - p] = NULLCHAR;
\r
1253 q = mfp->faceName;
\r
1254 while (*p && !isdigit(*p)) {
\r
1256 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1257 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1259 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1262 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1263 mfp->pointSize = (float) atof(p);
\r
1264 mfp->bold = (strchr(p, 'b') != NULL);
\r
1265 mfp->italic = (strchr(p, 'i') != NULL);
\r
1266 mfp->underline = (strchr(p, 'u') != NULL);
\r
1267 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1268 mfp->charset = DEFAULT_CHARSET;
\r
1269 q = strchr(p, 'c');
\r
1271 mfp->charset = (BYTE) atoi(q+1);
\r
1275 ParseFont(char *name, int number)
\r
1276 { // wrapper to shield back-end from 'font'
\r
1277 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1282 { // in WB we have a 2D array of fonts; this initializes their description
\r
1284 /* Point font array elements to structures and
\r
1285 parse default font names */
\r
1286 for (i=0; i<NUM_FONTS; i++) {
\r
1287 for (j=0; j<NUM_SIZES; j++) {
\r
1288 font[j][i] = &fontRec[j][i];
\r
1289 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1296 { // here we create the actual fonts from the selected descriptions
\r
1298 for (i=0; i<NUM_FONTS; i++) {
\r
1299 for (j=0; j<NUM_SIZES; j++) {
\r
1300 CreateFontInMF(font[j][i]);
\r
1304 /* Color name parser.
\r
1305 X version accepts X color names, but this one
\r
1306 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1308 ParseColorName(char *name)
\r
1310 int red, green, blue, count;
\r
1311 char buf[MSG_SIZ];
\r
1313 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1315 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1316 &red, &green, &blue);
\r
1319 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1320 DisplayError(buf, 0);
\r
1321 return RGB(0, 0, 0);
\r
1323 return PALETTERGB(red, green, blue);
\r
1327 ParseColor(int n, char *name)
\r
1328 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1329 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1333 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1335 char *e = argValue;
\r
1339 if (*e == 'b') eff |= CFE_BOLD;
\r
1340 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1341 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1342 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1343 else if (*e == '#' || isdigit(*e)) break;
\r
1347 *color = ParseColorName(e);
\r
1351 ParseTextAttribs(ColorClass cc, char *s)
\r
1352 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1353 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1354 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1358 ParseBoardSize(void *addr, char *name)
\r
1359 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1360 BoardSize bs = SizeTiny;
\r
1361 while (sizeInfo[bs].name != NULL) {
\r
1362 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1363 *(BoardSize *)addr = bs;
\r
1368 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1373 { // [HGM] import name from appData first
\r
1376 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1377 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1378 textAttribs[cc].sound.data = NULL;
\r
1379 MyLoadSound(&textAttribs[cc].sound);
\r
1381 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1382 textAttribs[cc].sound.name = strdup("");
\r
1383 textAttribs[cc].sound.data = NULL;
\r
1385 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1386 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1387 sounds[sc].data = NULL;
\r
1388 MyLoadSound(&sounds[sc]);
\r
1393 SetCommPortDefaults()
\r
1395 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1396 dcb.DCBlength = sizeof(DCB);
\r
1397 dcb.BaudRate = 9600;
\r
1398 dcb.fBinary = TRUE;
\r
1399 dcb.fParity = FALSE;
\r
1400 dcb.fOutxCtsFlow = FALSE;
\r
1401 dcb.fOutxDsrFlow = FALSE;
\r
1402 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1403 dcb.fDsrSensitivity = FALSE;
\r
1404 dcb.fTXContinueOnXoff = TRUE;
\r
1405 dcb.fOutX = FALSE;
\r
1407 dcb.fNull = FALSE;
\r
1408 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1409 dcb.fAbortOnError = FALSE;
\r
1411 dcb.Parity = SPACEPARITY;
\r
1412 dcb.StopBits = ONESTOPBIT;
\r
1415 // [HGM] args: these three cases taken out to stay in front-end
\r
1417 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1418 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1419 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1420 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1422 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1423 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1424 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1425 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1426 ad->argName, mfp->faceName, mfp->pointSize,
\r
1427 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1428 mfp->bold ? "b" : "",
\r
1429 mfp->italic ? "i" : "",
\r
1430 mfp->underline ? "u" : "",
\r
1431 mfp->strikeout ? "s" : "",
\r
1432 (int)mfp->charset);
\r
1438 { // [HGM] copy the names from the internal WB variables to appData
\r
1441 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1442 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1443 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1444 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1448 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1449 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1450 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1451 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1452 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1453 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1454 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1455 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1456 (ta->effects) ? " " : "",
\r
1457 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1461 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1462 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1463 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1464 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1465 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1469 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1470 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1471 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1475 ParseCommPortSettings(char *s)
\r
1476 { // wrapper to keep dcb from back-end
\r
1477 ParseCommSettings(s, &dcb);
\r
1482 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1483 GetActualPlacement(hwndMain, &wpMain);
\r
1484 GetActualPlacement(hwndConsole, &wpConsole);
\r
1485 GetActualPlacement(commentDialog, &wpComment);
\r
1486 GetActualPlacement(editTagsDialog, &wpTags);
\r
1487 GetActualPlacement(gameListDialog, &wpGameList);
\r
1488 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1489 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1490 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1494 PrintCommPortSettings(FILE *f, char *name)
\r
1495 { // wrapper to shield back-end from DCB
\r
1496 PrintCommSettings(f, name, &dcb);
\r
1500 MySearchPath(char *installDir, char *name, char *fullname)
\r
1502 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1503 if(name[0]== '%') {
\r
1504 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1505 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1506 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1507 *strchr(buf, '%') = 0;
\r
1508 strcat(fullname, getenv(buf));
\r
1509 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1511 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1512 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1513 return (int) strlen(fullname);
\r
1515 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1519 MyGetFullPathName(char *name, char *fullname)
\r
1522 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1527 { // [HGM] args: allows testing if main window is realized from back-end
\r
1528 return hwndMain != NULL;
\r
1532 PopUpStartupDialog()
\r
1536 LoadLanguageFile(appData.language);
\r
1537 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1538 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1539 FreeProcInstance(lpProc);
\r
1542 /*---------------------------------------------------------------------------*\
\r
1544 * GDI board drawing routines
\r
1546 \*---------------------------------------------------------------------------*/
\r
1548 /* [AS] Draw square using background texture */
\r
1549 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1554 return; /* Should never happen! */
\r
1557 SetGraphicsMode( dst, GM_ADVANCED );
\r
1564 /* X reflection */
\r
1569 x.eDx = (FLOAT) dw + dx - 1;
\r
1572 SetWorldTransform( dst, &x );
\r
1575 /* Y reflection */
\r
1581 x.eDy = (FLOAT) dh + dy - 1;
\r
1583 SetWorldTransform( dst, &x );
\r
1591 x.eDx = (FLOAT) dx;
\r
1592 x.eDy = (FLOAT) dy;
\r
1595 SetWorldTransform( dst, &x );
\r
1599 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1607 SetWorldTransform( dst, &x );
\r
1609 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1612 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1614 PM_WP = (int) WhitePawn,
\r
1615 PM_WN = (int) WhiteKnight,
\r
1616 PM_WB = (int) WhiteBishop,
\r
1617 PM_WR = (int) WhiteRook,
\r
1618 PM_WQ = (int) WhiteQueen,
\r
1619 PM_WF = (int) WhiteFerz,
\r
1620 PM_WW = (int) WhiteWazir,
\r
1621 PM_WE = (int) WhiteAlfil,
\r
1622 PM_WM = (int) WhiteMan,
\r
1623 PM_WO = (int) WhiteCannon,
\r
1624 PM_WU = (int) WhiteUnicorn,
\r
1625 PM_WH = (int) WhiteNightrider,
\r
1626 PM_WA = (int) WhiteAngel,
\r
1627 PM_WC = (int) WhiteMarshall,
\r
1628 PM_WAB = (int) WhiteCardinal,
\r
1629 PM_WD = (int) WhiteDragon,
\r
1630 PM_WL = (int) WhiteLance,
\r
1631 PM_WS = (int) WhiteCobra,
\r
1632 PM_WV = (int) WhiteFalcon,
\r
1633 PM_WSG = (int) WhiteSilver,
\r
1634 PM_WG = (int) WhiteGrasshopper,
\r
1635 PM_WK = (int) WhiteKing,
\r
1636 PM_BP = (int) BlackPawn,
\r
1637 PM_BN = (int) BlackKnight,
\r
1638 PM_BB = (int) BlackBishop,
\r
1639 PM_BR = (int) BlackRook,
\r
1640 PM_BQ = (int) BlackQueen,
\r
1641 PM_BF = (int) BlackFerz,
\r
1642 PM_BW = (int) BlackWazir,
\r
1643 PM_BE = (int) BlackAlfil,
\r
1644 PM_BM = (int) BlackMan,
\r
1645 PM_BO = (int) BlackCannon,
\r
1646 PM_BU = (int) BlackUnicorn,
\r
1647 PM_BH = (int) BlackNightrider,
\r
1648 PM_BA = (int) BlackAngel,
\r
1649 PM_BC = (int) BlackMarshall,
\r
1650 PM_BG = (int) BlackGrasshopper,
\r
1651 PM_BAB = (int) BlackCardinal,
\r
1652 PM_BD = (int) BlackDragon,
\r
1653 PM_BL = (int) BlackLance,
\r
1654 PM_BS = (int) BlackCobra,
\r
1655 PM_BV = (int) BlackFalcon,
\r
1656 PM_BSG = (int) BlackSilver,
\r
1657 PM_BK = (int) BlackKing
\r
1660 static HFONT hPieceFont = NULL;
\r
1661 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1662 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1663 static int fontBitmapSquareSize = 0;
\r
1664 static char pieceToFontChar[(int) EmptySquare] =
\r
1665 { 'p', 'n', 'b', 'r', 'q',
\r
1666 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1667 'k', 'o', 'm', 'v', 't', 'w',
\r
1668 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1671 extern BOOL SetCharTable( char *table, const char * map );
\r
1672 /* [HGM] moved to backend.c */
\r
1674 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1677 BYTE r1 = GetRValue( color );
\r
1678 BYTE g1 = GetGValue( color );
\r
1679 BYTE b1 = GetBValue( color );
\r
1685 /* Create a uniform background first */
\r
1686 hbrush = CreateSolidBrush( color );
\r
1687 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1688 FillRect( hdc, &rc, hbrush );
\r
1689 DeleteObject( hbrush );
\r
1692 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1693 int steps = squareSize / 2;
\r
1696 for( i=0; i<steps; i++ ) {
\r
1697 BYTE r = r1 - (r1-r2) * i / steps;
\r
1698 BYTE g = g1 - (g1-g2) * i / steps;
\r
1699 BYTE b = b1 - (b1-b2) * i / steps;
\r
1701 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1702 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1703 FillRect( hdc, &rc, hbrush );
\r
1704 DeleteObject(hbrush);
\r
1707 else if( mode == 2 ) {
\r
1708 /* Diagonal gradient, good more or less for every piece */
\r
1709 POINT triangle[3];
\r
1710 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1711 HBRUSH hbrush_old;
\r
1712 int steps = squareSize;
\r
1715 triangle[0].x = squareSize - steps;
\r
1716 triangle[0].y = squareSize;
\r
1717 triangle[1].x = squareSize;
\r
1718 triangle[1].y = squareSize;
\r
1719 triangle[2].x = squareSize;
\r
1720 triangle[2].y = squareSize - steps;
\r
1722 for( i=0; i<steps; i++ ) {
\r
1723 BYTE r = r1 - (r1-r2) * i / steps;
\r
1724 BYTE g = g1 - (g1-g2) * i / steps;
\r
1725 BYTE b = b1 - (b1-b2) * i / steps;
\r
1727 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1728 hbrush_old = SelectObject( hdc, hbrush );
\r
1729 Polygon( hdc, triangle, 3 );
\r
1730 SelectObject( hdc, hbrush_old );
\r
1731 DeleteObject(hbrush);
\r
1736 SelectObject( hdc, hpen );
\r
1741 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1742 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1743 piece: follow the steps as explained below.
\r
1745 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1749 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1753 int backColor = whitePieceColor;
\r
1754 int foreColor = blackPieceColor;
\r
1756 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1757 backColor = appData.fontBackColorWhite;
\r
1758 foreColor = appData.fontForeColorWhite;
\r
1760 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1761 backColor = appData.fontBackColorBlack;
\r
1762 foreColor = appData.fontForeColorBlack;
\r
1766 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1768 hbm_old = SelectObject( hdc, hbm );
\r
1772 rc.right = squareSize;
\r
1773 rc.bottom = squareSize;
\r
1775 /* Step 1: background is now black */
\r
1776 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1778 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1780 pt.x = (squareSize - sz.cx) / 2;
\r
1781 pt.y = (squareSize - sz.cy) / 2;
\r
1783 SetBkMode( hdc, TRANSPARENT );
\r
1784 SetTextColor( hdc, chroma );
\r
1785 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1786 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1788 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1789 /* Step 3: the area outside the piece is filled with white */
\r
1790 // FloodFill( hdc, 0, 0, chroma );
\r
1791 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1792 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1793 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1794 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1795 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1797 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1798 but if the start point is not inside the piece we're lost!
\r
1799 There should be a better way to do this... if we could create a region or path
\r
1800 from the fill operation we would be fine for example.
\r
1802 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1803 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1805 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1806 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1807 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1809 SelectObject( dc2, bm2 );
\r
1810 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1811 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1812 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1813 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1814 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1817 DeleteObject( bm2 );
\r
1820 SetTextColor( hdc, 0 );
\r
1822 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1823 draw the piece again in black for safety.
\r
1825 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1827 SelectObject( hdc, hbm_old );
\r
1829 if( hPieceMask[index] != NULL ) {
\r
1830 DeleteObject( hPieceMask[index] );
\r
1833 hPieceMask[index] = hbm;
\r
1836 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1838 SelectObject( hdc, hbm );
\r
1841 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1842 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1843 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1845 SelectObject( dc1, hPieceMask[index] );
\r
1846 SelectObject( dc2, bm2 );
\r
1847 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1848 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1851 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1852 the piece background and deletes (makes transparent) the rest.
\r
1853 Thanks to that mask, we are free to paint the background with the greates
\r
1854 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1855 We use this, to make gradients and give the pieces a "roundish" look.
\r
1857 SetPieceBackground( hdc, backColor, 2 );
\r
1858 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1862 DeleteObject( bm2 );
\r
1865 SetTextColor( hdc, foreColor );
\r
1866 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1868 SelectObject( hdc, hbm_old );
\r
1870 if( hPieceFace[index] != NULL ) {
\r
1871 DeleteObject( hPieceFace[index] );
\r
1874 hPieceFace[index] = hbm;
\r
1877 static int TranslatePieceToFontPiece( int piece )
\r
1907 case BlackMarshall:
\r
1911 case BlackNightrider:
\r
1917 case BlackUnicorn:
\r
1921 case BlackGrasshopper:
\r
1933 case BlackCardinal:
\r
1940 case WhiteMarshall:
\r
1944 case WhiteNightrider:
\r
1950 case WhiteUnicorn:
\r
1954 case WhiteGrasshopper:
\r
1966 case WhiteCardinal:
\r
1975 void CreatePiecesFromFont()
\r
1978 HDC hdc_window = NULL;
\r
1984 if( fontBitmapSquareSize < 0 ) {
\r
1985 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1989 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1990 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1991 fontBitmapSquareSize = -1;
\r
1995 if( fontBitmapSquareSize != squareSize ) {
\r
1996 hdc_window = GetDC( hwndMain );
\r
1997 hdc = CreateCompatibleDC( hdc_window );
\r
1999 if( hPieceFont != NULL ) {
\r
2000 DeleteObject( hPieceFont );
\r
2003 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2004 hPieceMask[i] = NULL;
\r
2005 hPieceFace[i] = NULL;
\r
2011 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2012 fontHeight = appData.fontPieceSize;
\r
2015 fontHeight = (fontHeight * squareSize) / 100;
\r
2017 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2019 lf.lfEscapement = 0;
\r
2020 lf.lfOrientation = 0;
\r
2021 lf.lfWeight = FW_NORMAL;
\r
2023 lf.lfUnderline = 0;
\r
2024 lf.lfStrikeOut = 0;
\r
2025 lf.lfCharSet = DEFAULT_CHARSET;
\r
2026 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2027 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2028 lf.lfQuality = PROOF_QUALITY;
\r
2029 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2030 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2031 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2033 hPieceFont = CreateFontIndirect( &lf );
\r
2035 if( hPieceFont == NULL ) {
\r
2036 fontBitmapSquareSize = -2;
\r
2039 /* Setup font-to-piece character table */
\r
2040 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2041 /* No (or wrong) global settings, try to detect the font */
\r
2042 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2044 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2046 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2047 /* DiagramTT* family */
\r
2048 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2050 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2051 /* Fairy symbols */
\r
2052 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2054 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2055 /* Good Companion (Some characters get warped as literal :-( */
\r
2056 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2057 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2058 SetCharTable(pieceToFontChar, s);
\r
2061 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2062 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2066 /* Create bitmaps */
\r
2067 hfont_old = SelectObject( hdc, hPieceFont );
\r
2068 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2069 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2070 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2072 SelectObject( hdc, hfont_old );
\r
2074 fontBitmapSquareSize = squareSize;
\r
2078 if( hdc != NULL ) {
\r
2082 if( hdc_window != NULL ) {
\r
2083 ReleaseDC( hwndMain, hdc_window );
\r
2088 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2092 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2093 if (gameInfo.event &&
\r
2094 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2095 strcmp(name, "k80s") == 0) {
\r
2096 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2098 return LoadBitmap(hinst, name);
\r
2102 /* Insert a color into the program's logical palette
\r
2103 structure. This code assumes the given color is
\r
2104 the result of the RGB or PALETTERGB macro, and it
\r
2105 knows how those macros work (which is documented).
\r
2108 InsertInPalette(COLORREF color)
\r
2110 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2112 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2113 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2114 pLogPal->palNumEntries--;
\r
2118 pe->peFlags = (char) 0;
\r
2119 pe->peRed = (char) (0xFF & color);
\r
2120 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2121 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2127 InitDrawingColors()
\r
2129 if (pLogPal == NULL) {
\r
2130 /* Allocate enough memory for a logical palette with
\r
2131 * PALETTESIZE entries and set the size and version fields
\r
2132 * of the logical palette structure.
\r
2134 pLogPal = (NPLOGPALETTE)
\r
2135 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2136 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2137 pLogPal->palVersion = 0x300;
\r
2139 pLogPal->palNumEntries = 0;
\r
2141 InsertInPalette(lightSquareColor);
\r
2142 InsertInPalette(darkSquareColor);
\r
2143 InsertInPalette(whitePieceColor);
\r
2144 InsertInPalette(blackPieceColor);
\r
2145 InsertInPalette(highlightSquareColor);
\r
2146 InsertInPalette(premoveHighlightColor);
\r
2148 /* create a logical color palette according the information
\r
2149 * in the LOGPALETTE structure.
\r
2151 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2153 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2154 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2155 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2156 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2157 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2158 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2159 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2160 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2161 /* [AS] Force rendering of the font-based pieces */
\r
2162 if( fontBitmapSquareSize > 0 ) {
\r
2163 fontBitmapSquareSize = 0;
\r
2169 BoardWidth(int boardSize, int n)
\r
2170 { /* [HGM] argument n added to allow different width and height */
\r
2171 int lineGap = sizeInfo[boardSize].lineGap;
\r
2173 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2174 lineGap = appData.overrideLineGap;
\r
2177 return (n + 1) * lineGap +
\r
2178 n * sizeInfo[boardSize].squareSize;
\r
2181 /* Respond to board resize by dragging edge */
\r
2183 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2185 BoardSize newSize = NUM_SIZES - 1;
\r
2186 static int recurse = 0;
\r
2187 if (IsIconic(hwndMain)) return;
\r
2188 if (recurse > 0) return;
\r
2190 while (newSize > 0) {
\r
2191 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2192 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2193 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2196 boardSize = newSize;
\r
2197 InitDrawingSizes(boardSize, flags);
\r
2202 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2205 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2207 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2208 ChessSquare piece;
\r
2209 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2211 SIZE clockSize, messageSize;
\r
2213 char buf[MSG_SIZ];
\r
2215 HMENU hmenu = GetMenu(hwndMain);
\r
2216 RECT crect, wrect, oldRect;
\r
2218 LOGBRUSH logbrush;
\r
2220 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2221 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2223 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2224 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2226 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2227 oldRect.top = wpMain.y;
\r
2228 oldRect.right = wpMain.x + wpMain.width;
\r
2229 oldRect.bottom = wpMain.y + wpMain.height;
\r
2231 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2232 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2233 squareSize = sizeInfo[boardSize].squareSize;
\r
2234 lineGap = sizeInfo[boardSize].lineGap;
\r
2235 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2237 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2238 lineGap = appData.overrideLineGap;
\r
2241 if (tinyLayout != oldTinyLayout) {
\r
2242 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2244 style &= ~WS_SYSMENU;
\r
2245 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2246 "&Minimize\tCtrl+F4");
\r
2248 style |= WS_SYSMENU;
\r
2249 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2251 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2253 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2254 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2255 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2257 DrawMenuBar(hwndMain);
\r
2260 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2261 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2263 /* Get text area sizes */
\r
2264 hdc = GetDC(hwndMain);
\r
2265 if (appData.clockMode) {
\r
2266 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2268 snprintf(buf, MSG_SIZ, _("White"));
\r
2270 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2271 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2272 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2273 str = _("We only care about the height here");
\r
2274 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2275 SelectObject(hdc, oldFont);
\r
2276 ReleaseDC(hwndMain, hdc);
\r
2278 /* Compute where everything goes */
\r
2279 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2280 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2281 logoHeight = 2*clockSize.cy;
\r
2282 leftLogoRect.left = OUTER_MARGIN;
\r
2283 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2284 leftLogoRect.top = OUTER_MARGIN;
\r
2285 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2287 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2288 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2289 rightLogoRect.top = OUTER_MARGIN;
\r
2290 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2293 whiteRect.left = leftLogoRect.right;
\r
2294 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2295 whiteRect.top = OUTER_MARGIN;
\r
2296 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2298 blackRect.right = rightLogoRect.left;
\r
2299 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2300 blackRect.top = whiteRect.top;
\r
2301 blackRect.bottom = whiteRect.bottom;
\r
2303 whiteRect.left = OUTER_MARGIN;
\r
2304 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2305 whiteRect.top = OUTER_MARGIN;
\r
2306 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2308 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2309 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2310 blackRect.top = whiteRect.top;
\r
2311 blackRect.bottom = whiteRect.bottom;
\r
2313 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2316 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2317 if (appData.showButtonBar) {
\r
2318 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2319 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2321 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2323 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2324 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2326 boardRect.left = OUTER_MARGIN;
\r
2327 boardRect.right = boardRect.left + boardWidth;
\r
2328 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2329 boardRect.bottom = boardRect.top + boardHeight;
\r
2331 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2332 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2333 oldBoardSize = boardSize;
\r
2334 oldTinyLayout = tinyLayout;
\r
2335 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2336 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2337 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2338 winW *= 1 + twoBoards;
\r
2339 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2340 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2341 wpMain.height = winH; // without disturbing window attachments
\r
2342 GetWindowRect(hwndMain, &wrect);
\r
2343 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2344 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2346 // [HGM] placement: let attached windows follow size change.
\r
2347 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2348 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2349 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2350 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2351 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2353 /* compensate if menu bar wrapped */
\r
2354 GetClientRect(hwndMain, &crect);
\r
2355 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2356 wpMain.height += offby;
\r
2358 case WMSZ_TOPLEFT:
\r
2359 SetWindowPos(hwndMain, NULL,
\r
2360 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2361 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2364 case WMSZ_TOPRIGHT:
\r
2366 SetWindowPos(hwndMain, NULL,
\r
2367 wrect.left, wrect.bottom - wpMain.height,
\r
2368 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2371 case WMSZ_BOTTOMLEFT:
\r
2373 SetWindowPos(hwndMain, NULL,
\r
2374 wrect.right - wpMain.width, wrect.top,
\r
2375 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2378 case WMSZ_BOTTOMRIGHT:
\r
2382 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2383 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2388 for (i = 0; i < N_BUTTONS; i++) {
\r
2389 if (buttonDesc[i].hwnd != NULL) {
\r
2390 DestroyWindow(buttonDesc[i].hwnd);
\r
2391 buttonDesc[i].hwnd = NULL;
\r
2393 if (appData.showButtonBar) {
\r
2394 buttonDesc[i].hwnd =
\r
2395 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2396 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2397 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2398 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2399 (HMENU) buttonDesc[i].id,
\r
2400 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2402 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2403 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2404 MAKELPARAM(FALSE, 0));
\r
2406 if (buttonDesc[i].id == IDM_Pause)
\r
2407 hwndPause = buttonDesc[i].hwnd;
\r
2408 buttonDesc[i].wndproc = (WNDPROC)
\r
2409 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2412 if (gridPen != NULL) DeleteObject(gridPen);
\r
2413 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2414 if (premovePen != NULL) DeleteObject(premovePen);
\r
2415 if (lineGap != 0) {
\r
2416 logbrush.lbStyle = BS_SOLID;
\r
2417 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2419 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2420 lineGap, &logbrush, 0, NULL);
\r
2421 logbrush.lbColor = highlightSquareColor;
\r
2423 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2424 lineGap, &logbrush, 0, NULL);
\r
2426 logbrush.lbColor = premoveHighlightColor;
\r
2428 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2429 lineGap, &logbrush, 0, NULL);
\r
2431 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2432 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2433 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2434 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2435 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2436 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2437 BOARD_WIDTH * (squareSize + lineGap);
\r
2438 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2440 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2441 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2442 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2443 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2444 lineGap / 2 + (i * (squareSize + lineGap));
\r
2445 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2446 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2447 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2451 /* [HGM] Licensing requirement */
\r
2453 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2456 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2458 GothicPopUp( "", VariantNormal);
\r
2461 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2463 /* Load piece bitmaps for this board size */
\r
2464 for (i=0; i<=2; i++) {
\r
2465 for (piece = WhitePawn;
\r
2466 (int) piece < (int) BlackPawn;
\r
2467 piece = (ChessSquare) ((int) piece + 1)) {
\r
2468 if (pieceBitmap[i][piece] != NULL)
\r
2469 DeleteObject(pieceBitmap[i][piece]);
\r
2473 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2474 // Orthodox Chess pieces
\r
2475 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2476 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2477 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2478 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2479 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2480 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2481 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2482 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2483 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2484 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2485 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2486 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2487 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2488 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2489 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2490 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2491 // in Shogi, Hijack the unused Queen for Lance
\r
2492 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2493 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2494 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2496 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2501 if(squareSize <= 72 && squareSize >= 33) {
\r
2502 /* A & C are available in most sizes now */
\r
2503 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2504 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2507 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2508 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2509 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2510 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2513 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2516 } else { // Smirf-like
\r
2517 if(gameInfo.variant == VariantSChess) {
\r
2518 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2522 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2527 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2528 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2531 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2532 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2533 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2534 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2535 } else { // WinBoard standard
\r
2536 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2543 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2544 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2562 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2575 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2576 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2585 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2589 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2590 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2591 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2592 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2595 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2598 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2603 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2604 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2605 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2606 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2607 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2608 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2609 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2610 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2611 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2612 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2613 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2614 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2615 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2616 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2617 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2621 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2622 /* special Shogi support in this size */
\r
2623 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2624 for (piece = WhitePawn;
\r
2625 (int) piece < (int) BlackPawn;
\r
2626 piece = (ChessSquare) ((int) piece + 1)) {
\r
2627 if (pieceBitmap[i][piece] != NULL)
\r
2628 DeleteObject(pieceBitmap[i][piece]);
\r
2631 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2639 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2640 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2641 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2642 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2643 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2644 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2645 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2653 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2654 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2655 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2656 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2657 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2658 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2659 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2667 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2668 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2669 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2670 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2671 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2672 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2678 PieceBitmap(ChessSquare p, int kind)
\r
2680 if ((int) p >= (int) BlackPawn)
\r
2681 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2683 return pieceBitmap[kind][(int) p];
\r
2686 /***************************************************************/
\r
2688 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2689 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2691 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2692 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2696 SquareToPos(int row, int column, int * x, int * y)
\r
2699 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2700 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2702 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2703 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2708 DrawCoordsOnDC(HDC hdc)
\r
2710 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2711 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2712 char str[2] = { NULLCHAR, NULLCHAR };
\r
2713 int oldMode, oldAlign, x, y, start, i;
\r
2717 if (!appData.showCoords)
\r
2720 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2722 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2723 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2724 oldAlign = GetTextAlign(hdc);
\r
2725 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2727 y = boardRect.top + lineGap;
\r
2728 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2730 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2731 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2732 str[0] = files[start + i];
\r
2733 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2734 y += squareSize + lineGap;
\r
2737 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2739 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2740 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2741 str[0] = ranks[start + i];
\r
2742 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2743 x += squareSize + lineGap;
\r
2746 SelectObject(hdc, oldBrush);
\r
2747 SetBkMode(hdc, oldMode);
\r
2748 SetTextAlign(hdc, oldAlign);
\r
2749 SelectObject(hdc, oldFont);
\r
2753 DrawGridOnDC(HDC hdc)
\r
2757 if (lineGap != 0) {
\r
2758 oldPen = SelectObject(hdc, gridPen);
\r
2759 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2760 SelectObject(hdc, oldPen);
\r
2764 #define HIGHLIGHT_PEN 0
\r
2765 #define PREMOVE_PEN 1
\r
2768 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2771 HPEN oldPen, hPen;
\r
2772 if (lineGap == 0) return;
\r
2774 x1 = boardRect.left +
\r
2775 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2776 y1 = boardRect.top +
\r
2777 lineGap/2 + y * (squareSize + lineGap);
\r
2779 x1 = boardRect.left +
\r
2780 lineGap/2 + x * (squareSize + lineGap);
\r
2781 y1 = boardRect.top +
\r
2782 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2784 hPen = pen ? premovePen : highlightPen;
\r
2785 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2786 MoveToEx(hdc, x1, y1, NULL);
\r
2787 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2788 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2789 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2790 LineTo(hdc, x1, y1);
\r
2791 SelectObject(hdc, oldPen);
\r
2795 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2798 for (i=0; i<2; i++) {
\r
2799 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2800 DrawHighlightOnDC(hdc, TRUE,
\r
2801 h->sq[i].x, h->sq[i].y,
\r
2806 /* Note: sqcolor is used only in monoMode */
\r
2807 /* Note that this code is largely duplicated in woptions.c,
\r
2808 function DrawSampleSquare, so that needs to be updated too */
\r
2810 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2812 HBITMAP oldBitmap;
\r
2816 if (appData.blindfold) return;
\r
2818 /* [AS] Use font-based pieces if needed */
\r
2819 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2820 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2821 CreatePiecesFromFont();
\r
2823 if( fontBitmapSquareSize == squareSize ) {
\r
2824 int index = TranslatePieceToFontPiece(piece);
\r
2826 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2828 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2829 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2833 squareSize, squareSize,
\r
2838 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2840 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2841 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2845 squareSize, squareSize,
\r
2854 if (appData.monoMode) {
\r
2855 SelectObject(tmphdc, PieceBitmap(piece,
\r
2856 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2857 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2858 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2860 tmpSize = squareSize;
\r
2862 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2863 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2864 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2865 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2866 x += (squareSize - minorSize)>>1;
\r
2867 y += squareSize - minorSize - 2;
\r
2868 tmpSize = minorSize;
\r
2870 if (color || appData.allWhite ) {
\r
2871 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2873 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2874 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2875 if(appData.upsideDown && color==flipView)
\r
2876 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2878 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2879 /* Use black for outline of white pieces */
\r
2880 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2881 if(appData.upsideDown && color==flipView)
\r
2882 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2884 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2886 /* Use square color for details of black pieces */
\r
2887 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2888 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2889 if(appData.upsideDown && !flipView)
\r
2890 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2892 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2894 SelectObject(hdc, oldBrush);
\r
2895 SelectObject(tmphdc, oldBitmap);
\r
2899 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2900 int GetBackTextureMode( int algo )
\r
2902 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2906 case BACK_TEXTURE_MODE_PLAIN:
\r
2907 result = 1; /* Always use identity map */
\r
2909 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2910 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2918 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2919 to handle redraws cleanly (as random numbers would always be different).
\r
2921 VOID RebuildTextureSquareInfo()
\r
2931 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2933 if( liteBackTexture != NULL ) {
\r
2934 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2935 lite_w = bi.bmWidth;
\r
2936 lite_h = bi.bmHeight;
\r
2940 if( darkBackTexture != NULL ) {
\r
2941 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2942 dark_w = bi.bmWidth;
\r
2943 dark_h = bi.bmHeight;
\r
2947 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2948 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2949 if( (col + row) & 1 ) {
\r
2951 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2952 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2953 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2955 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2956 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2957 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2959 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2960 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2965 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2966 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2967 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2969 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2970 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2971 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2973 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2974 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2981 /* [AS] Arrow highlighting support */
\r
2983 static double A_WIDTH = 5; /* Width of arrow body */
\r
2985 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2986 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2988 static double Sqr( double x )
\r
2993 static int Round( double x )
\r
2995 return (int) (x + 0.5);
\r
2998 /* Draw an arrow between two points using current settings */
\r
2999 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3002 double dx, dy, j, k, x, y;
\r
3004 if( d_x == s_x ) {
\r
3005 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3007 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3010 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3011 arrow[1].y = d_y - h;
\r
3013 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3014 arrow[2].y = d_y - h;
\r
3019 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3020 arrow[5].y = d_y - h;
\r
3022 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3023 arrow[4].y = d_y - h;
\r
3025 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3028 else if( d_y == s_y ) {
\r
3029 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3032 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3034 arrow[1].x = d_x - w;
\r
3035 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3037 arrow[2].x = d_x - w;
\r
3038 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3043 arrow[5].x = d_x - w;
\r
3044 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3046 arrow[4].x = d_x - w;
\r
3047 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3050 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3053 /* [AS] Needed a lot of paper for this! :-) */
\r
3054 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3055 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3057 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3059 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3064 arrow[0].x = Round(x - j);
\r
3065 arrow[0].y = Round(y + j*dx);
\r
3067 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3068 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3071 x = (double) d_x - k;
\r
3072 y = (double) d_y - k*dy;
\r
3075 x = (double) d_x + k;
\r
3076 y = (double) d_y + k*dy;
\r
3079 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3081 arrow[6].x = Round(x - j);
\r
3082 arrow[6].y = Round(y + j*dx);
\r
3084 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3085 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3087 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3088 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3093 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3094 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3097 Polygon( hdc, arrow, 7 );
\r
3100 /* [AS] Draw an arrow between two squares */
\r
3101 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3103 int s_x, s_y, d_x, d_y;
\r
3110 if( s_col == d_col && s_row == d_row ) {
\r
3114 /* Get source and destination points */
\r
3115 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3116 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3119 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3121 else if( d_y < s_y ) {
\r
3122 d_y += squareSize / 2 + squareSize / 4;
\r
3125 d_y += squareSize / 2;
\r
3129 d_x += squareSize / 2 - squareSize / 4;
\r
3131 else if( d_x < s_x ) {
\r
3132 d_x += squareSize / 2 + squareSize / 4;
\r
3135 d_x += squareSize / 2;
\r
3138 s_x += squareSize / 2;
\r
3139 s_y += squareSize / 2;
\r
3141 /* Adjust width */
\r
3142 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3145 stLB.lbStyle = BS_SOLID;
\r
3146 stLB.lbColor = appData.highlightArrowColor;
\r
3149 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3150 holdpen = SelectObject( hdc, hpen );
\r
3151 hbrush = CreateBrushIndirect( &stLB );
\r
3152 holdbrush = SelectObject( hdc, hbrush );
\r
3154 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3156 SelectObject( hdc, holdpen );
\r
3157 SelectObject( hdc, holdbrush );
\r
3158 DeleteObject( hpen );
\r
3159 DeleteObject( hbrush );
\r
3162 BOOL HasHighlightInfo()
\r
3164 BOOL result = FALSE;
\r
3166 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3167 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3175 BOOL IsDrawArrowEnabled()
\r
3177 BOOL result = FALSE;
\r
3179 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3186 VOID DrawArrowHighlight( HDC hdc )
\r
3188 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3189 DrawArrowBetweenSquares( hdc,
\r
3190 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3191 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3195 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3197 HRGN result = NULL;
\r
3199 if( HasHighlightInfo() ) {
\r
3200 int x1, y1, x2, y2;
\r
3201 int sx, sy, dx, dy;
\r
3203 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3204 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3206 sx = MIN( x1, x2 );
\r
3207 sy = MIN( y1, y2 );
\r
3208 dx = MAX( x1, x2 ) + squareSize;
\r
3209 dy = MAX( y1, y2 ) + squareSize;
\r
3211 result = CreateRectRgn( sx, sy, dx, dy );
\r
3218 Warning: this function modifies the behavior of several other functions.
\r
3220 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3221 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3222 repaint is scattered all over the place, which is not good for features such as
\r
3223 "arrow highlighting" that require a full repaint of the board.
\r
3225 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3226 user interaction, when speed is not so important) but especially to avoid errors
\r
3227 in the displayed graphics.
\r
3229 In such patched places, I always try refer to this function so there is a single
\r
3230 place to maintain knowledge.
\r
3232 To restore the original behavior, just return FALSE unconditionally.
\r
3234 BOOL IsFullRepaintPreferrable()
\r
3236 BOOL result = FALSE;
\r
3238 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3239 /* Arrow may appear on the board */
\r
3247 This function is called by DrawPosition to know whether a full repaint must
\r
3250 Only DrawPosition may directly call this function, which makes use of
\r
3251 some state information. Other function should call DrawPosition specifying
\r
3252 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3254 BOOL DrawPositionNeedsFullRepaint()
\r
3256 BOOL result = FALSE;
\r
3259 Probably a slightly better policy would be to trigger a full repaint
\r
3260 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3261 but animation is fast enough that it's difficult to notice.
\r
3263 if( animInfo.piece == EmptySquare ) {
\r
3264 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3273 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3275 int row, column, x, y, square_color, piece_color;
\r
3276 ChessSquare piece;
\r
3278 HDC texture_hdc = NULL;
\r
3280 /* [AS] Initialize background textures if needed */
\r
3281 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3282 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3283 if( backTextureSquareSize != squareSize
\r
3284 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3285 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3286 backTextureSquareSize = squareSize;
\r
3287 RebuildTextureSquareInfo();
\r
3290 texture_hdc = CreateCompatibleDC( hdc );
\r
3293 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3294 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3296 SquareToPos(row, column, &x, &y);
\r
3298 piece = board[row][column];
\r
3300 square_color = ((column + row) % 2) == 1;
\r
3301 if( gameInfo.variant == VariantXiangqi ) {
\r
3302 square_color = !InPalace(row, column);
\r
3303 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3304 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3306 piece_color = (int) piece < (int) BlackPawn;
\r
3309 /* [HGM] holdings file: light square or black */
\r
3310 if(column == BOARD_LEFT-2) {
\r
3311 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3314 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3318 if(column == BOARD_RGHT + 1 ) {
\r
3319 if( row < gameInfo.holdingsSize )
\r
3322 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3326 if(column == BOARD_LEFT-1 ) /* left align */
\r
3327 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3328 else if( column == BOARD_RGHT) /* right align */
\r
3329 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3331 if (appData.monoMode) {
\r
3332 if (piece == EmptySquare) {
\r
3333 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3334 square_color ? WHITENESS : BLACKNESS);
\r
3336 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3339 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3340 /* [AS] Draw the square using a texture bitmap */
\r
3341 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3342 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3343 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3346 squareSize, squareSize,
\r
3349 backTextureSquareInfo[r][c].mode,
\r
3350 backTextureSquareInfo[r][c].x,
\r
3351 backTextureSquareInfo[r][c].y );
\r
3353 SelectObject( texture_hdc, hbm );
\r
3355 if (piece != EmptySquare) {
\r
3356 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3360 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3362 oldBrush = SelectObject(hdc, brush );
\r
3363 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3364 SelectObject(hdc, oldBrush);
\r
3365 if (piece != EmptySquare)
\r
3366 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3371 if( texture_hdc != NULL ) {
\r
3372 DeleteDC( texture_hdc );
\r
3376 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3377 void fputDW(FILE *f, int x)
\r
3379 fputc(x & 255, f);
\r
3380 fputc(x>>8 & 255, f);
\r
3381 fputc(x>>16 & 255, f);
\r
3382 fputc(x>>24 & 255, f);
\r
3385 #define MAX_CLIPS 200 /* more than enough */
\r
3388 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3390 // HBITMAP bufferBitmap;
\r
3395 int w = 100, h = 50;
\r
3397 if(logo == NULL) {
\r
3398 if(!logoHeight) return;
\r
3399 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3401 // GetClientRect(hwndMain, &Rect);
\r
3402 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3403 // Rect.bottom-Rect.top+1);
\r
3404 tmphdc = CreateCompatibleDC(hdc);
\r
3405 hbm = SelectObject(tmphdc, logo);
\r
3406 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3410 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3411 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3412 SelectObject(tmphdc, hbm);
\r
3420 HDC hdc = GetDC(hwndMain);
\r
3421 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3422 if(appData.autoLogo) {
\r
3424 switch(gameMode) { // pick logos based on game mode
\r
3425 case IcsObserving:
\r
3426 whiteLogo = second.programLogo; // ICS logo
\r
3427 blackLogo = second.programLogo;
\r
3430 case IcsPlayingWhite:
\r
3431 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3432 blackLogo = second.programLogo; // ICS logo
\r
3434 case IcsPlayingBlack:
\r
3435 whiteLogo = second.programLogo; // ICS logo
\r
3436 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3438 case TwoMachinesPlay:
\r
3439 if(first.twoMachinesColor[0] == 'b') {
\r
3440 whiteLogo = second.programLogo;
\r
3441 blackLogo = first.programLogo;
\r
3444 case MachinePlaysWhite:
\r
3445 blackLogo = userLogo;
\r
3447 case MachinePlaysBlack:
\r
3448 whiteLogo = userLogo;
\r
3449 blackLogo = first.programLogo;
\r
3452 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3453 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3454 ReleaseDC(hwndMain, hdc);
\r
3459 UpdateLogos(int display)
\r
3460 { // called after loading new engine(s), in tourney or from menu
\r
3461 LoadLogo(&first, 0, FALSE);
\r
3462 LoadLogo(&second, 1, appData.icsActive);
\r
3463 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3464 if(display) DisplayLogos();
\r
3467 static HDC hdcSeek;
\r
3469 // [HGM] seekgraph
\r
3470 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3473 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3474 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3475 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3476 SelectObject( hdcSeek, hp );
\r
3479 // front-end wrapper for drawing functions to do rectangles
\r
3480 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3485 if (hdcSeek == NULL) {
\r
3486 hdcSeek = GetDC(hwndMain);
\r
3487 if (!appData.monoMode) {
\r
3488 SelectPalette(hdcSeek, hPal, FALSE);
\r
3489 RealizePalette(hdcSeek);
\r
3492 hp = SelectObject( hdcSeek, gridPen );
\r
3493 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3494 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3495 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3496 SelectObject( hdcSeek, hp );
\r
3499 // front-end wrapper for putting text in graph
\r
3500 void DrawSeekText(char *buf, int x, int y)
\r
3503 SetBkMode( hdcSeek, TRANSPARENT );
\r
3504 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3505 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3508 void DrawSeekDot(int x, int y, int color)
\r
3510 int square = color & 0x80;
\r
3511 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3512 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3515 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3516 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3518 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3519 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3520 SelectObject(hdcSeek, oldBrush);
\r
3524 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3526 static Board lastReq[2], lastDrawn[2];
\r
3527 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3528 static int lastDrawnFlipView = 0;
\r
3529 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3530 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3533 HBITMAP bufferBitmap;
\r
3534 HBITMAP oldBitmap;
\r
3536 HRGN clips[MAX_CLIPS];
\r
3537 ChessSquare dragged_piece = EmptySquare;
\r
3538 int nr = twoBoards*partnerUp;
\r
3540 /* I'm undecided on this - this function figures out whether a full
\r
3541 * repaint is necessary on its own, so there's no real reason to have the
\r
3542 * caller tell it that. I think this can safely be set to FALSE - but
\r
3543 * if we trust the callers not to request full repaints unnessesarily, then
\r
3544 * we could skip some clipping work. In other words, only request a full
\r
3545 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3546 * gamestart and similar) --Hawk
\r
3548 Boolean fullrepaint = repaint;
\r
3550 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3552 if( DrawPositionNeedsFullRepaint() ) {
\r
3553 fullrepaint = TRUE;
\r
3556 if (board == NULL) {
\r
3557 if (!lastReqValid[nr]) {
\r
3560 board = lastReq[nr];
\r
3562 CopyBoard(lastReq[nr], board);
\r
3563 lastReqValid[nr] = 1;
\r
3566 if (doingSizing) {
\r
3570 if (IsIconic(hwndMain)) {
\r
3574 if (hdc == NULL) {
\r
3575 hdc = GetDC(hwndMain);
\r
3576 if (!appData.monoMode) {
\r
3577 SelectPalette(hdc, hPal, FALSE);
\r
3578 RealizePalette(hdc);
\r
3582 releaseDC = FALSE;
\r
3585 /* Create some work-DCs */
\r
3586 hdcmem = CreateCompatibleDC(hdc);
\r
3587 tmphdc = CreateCompatibleDC(hdc);
\r
3589 /* If dragging is in progress, we temporarely remove the piece */
\r
3590 /* [HGM] or temporarily decrease count if stacked */
\r
3591 /* !! Moved to before board compare !! */
\r
3592 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3593 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3594 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3595 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3596 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3598 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3599 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3600 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3602 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3605 /* Figure out which squares need updating by comparing the
\r
3606 * newest board with the last drawn board and checking if
\r
3607 * flipping has changed.
\r
3609 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3610 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3611 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3612 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3613 SquareToPos(row, column, &x, &y);
\r
3614 clips[num_clips++] =
\r
3615 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3619 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3620 for (i=0; i<2; i++) {
\r
3621 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3622 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3623 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3624 lastDrawnHighlight.sq[i].y >= 0) {
\r
3625 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3626 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3627 clips[num_clips++] =
\r
3628 CreateRectRgn(x - lineGap, y - lineGap,
\r
3629 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3631 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3632 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3633 clips[num_clips++] =
\r
3634 CreateRectRgn(x - lineGap, y - lineGap,
\r
3635 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3639 for (i=0; i<2; i++) {
\r
3640 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3641 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3642 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3643 lastDrawnPremove.sq[i].y >= 0) {
\r
3644 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3645 lastDrawnPremove.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
3650 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3651 premoveHighlightInfo.sq[i].y >= 0) {
\r
3652 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3653 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3654 clips[num_clips++] =
\r
3655 CreateRectRgn(x - lineGap, y - lineGap,
\r
3656 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3660 } else { // nr == 1
\r
3661 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3662 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3663 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3664 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3665 for (i=0; i<2; i++) {
\r
3666 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3667 partnerHighlightInfo.sq[i].y >= 0) {
\r
3668 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3669 partnerHighlightInfo.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
3674 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3675 oldPartnerHighlight.sq[i].y >= 0) {
\r
3676 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3677 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3678 clips[num_clips++] =
\r
3679 CreateRectRgn(x - lineGap, y - lineGap,
\r
3680 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3685 fullrepaint = TRUE;
\r
3688 /* Create a buffer bitmap - this is the actual bitmap
\r
3689 * being written to. When all the work is done, we can
\r
3690 * copy it to the real DC (the screen). This avoids
\r
3691 * the problems with flickering.
\r
3693 GetClientRect(hwndMain, &Rect);
\r
3694 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3695 Rect.bottom-Rect.top+1);
\r
3696 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3697 if (!appData.monoMode) {
\r
3698 SelectPalette(hdcmem, hPal, FALSE);
\r
3701 /* Create clips for dragging */
\r
3702 if (!fullrepaint) {
\r
3703 if (dragInfo.from.x >= 0) {
\r
3704 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3705 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3707 if (dragInfo.start.x >= 0) {
\r
3708 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3709 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3711 if (dragInfo.pos.x >= 0) {
\r
3712 x = dragInfo.pos.x - squareSize / 2;
\r
3713 y = dragInfo.pos.y - squareSize / 2;
\r
3714 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3716 if (dragInfo.lastpos.x >= 0) {
\r
3717 x = dragInfo.lastpos.x - squareSize / 2;
\r
3718 y = dragInfo.lastpos.y - squareSize / 2;
\r
3719 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3723 /* Are we animating a move?
\r
3725 * - remove the piece from the board (temporarely)
\r
3726 * - calculate the clipping region
\r
3728 if (!fullrepaint) {
\r
3729 if (animInfo.piece != EmptySquare) {
\r
3730 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3731 x = boardRect.left + animInfo.lastpos.x;
\r
3732 y = boardRect.top + animInfo.lastpos.y;
\r
3733 x2 = boardRect.left + animInfo.pos.x;
\r
3734 y2 = boardRect.top + animInfo.pos.y;
\r
3735 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3736 /* Slight kludge. The real problem is that after AnimateMove is
\r
3737 done, the position on the screen does not match lastDrawn.
\r
3738 This currently causes trouble only on e.p. captures in
\r
3739 atomic, where the piece moves to an empty square and then
\r
3740 explodes. The old and new positions both had an empty square
\r
3741 at the destination, but animation has drawn a piece there and
\r
3742 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3743 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3747 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3748 if (num_clips == 0)
\r
3749 fullrepaint = TRUE;
\r
3751 /* Set clipping on the memory DC */
\r
3752 if (!fullrepaint) {
\r
3753 SelectClipRgn(hdcmem, clips[0]);
\r
3754 for (x = 1; x < num_clips; x++) {
\r
3755 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3756 abort(); // this should never ever happen!
\r
3760 /* Do all the drawing to the memory DC */
\r
3761 if(explodeInfo.radius) { // [HGM] atomic
\r
3763 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3764 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3765 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3766 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3767 x += squareSize/2;
\r
3768 y += squareSize/2;
\r
3769 if(!fullrepaint) {
\r
3770 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3771 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3773 DrawGridOnDC(hdcmem);
\r
3774 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3775 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3776 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3777 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3778 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3779 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3780 SelectObject(hdcmem, oldBrush);
\r
3782 DrawGridOnDC(hdcmem);
\r
3783 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3784 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3785 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3787 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3788 oldPartnerHighlight = partnerHighlightInfo;
\r
3790 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3792 if(nr == 0) // [HGM] dual: markers only on left board
\r
3793 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3794 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3795 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3796 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3797 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3798 SquareToPos(row, column, &x, &y);
\r
3799 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3800 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3801 SelectObject(hdcmem, oldBrush);
\r
3806 if( appData.highlightMoveWithArrow ) {
\r
3807 DrawArrowHighlight(hdcmem);
\r
3810 DrawCoordsOnDC(hdcmem);
\r
3812 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3813 /* to make sure lastDrawn contains what is actually drawn */
\r
3815 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3816 if (dragged_piece != EmptySquare) {
\r
3817 /* [HGM] or restack */
\r
3818 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3819 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3821 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3822 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3823 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3824 x = dragInfo.pos.x - squareSize / 2;
\r
3825 y = dragInfo.pos.y - squareSize / 2;
\r
3826 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3827 ((int) dragInfo.piece < (int) BlackPawn),
\r
3828 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3831 /* Put the animated piece back into place and draw it */
\r
3832 if (animInfo.piece != EmptySquare) {
\r
3833 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3834 x = boardRect.left + animInfo.pos.x;
\r
3835 y = boardRect.top + animInfo.pos.y;
\r
3836 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3837 ((int) animInfo.piece < (int) BlackPawn),
\r
3838 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3841 /* Release the bufferBitmap by selecting in the old bitmap
\r
3842 * and delete the memory DC
\r
3844 SelectObject(hdcmem, oldBitmap);
\r
3847 /* Set clipping on the target DC */
\r
3848 if (!fullrepaint) {
\r
3849 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3851 GetRgnBox(clips[x], &rect);
\r
3852 DeleteObject(clips[x]);
\r
3853 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3854 rect.right + wpMain.width/2, rect.bottom);
\r
3856 SelectClipRgn(hdc, clips[0]);
\r
3857 for (x = 1; x < num_clips; x++) {
\r
3858 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3859 abort(); // this should never ever happen!
\r
3863 /* Copy the new bitmap onto the screen in one go.
\r
3864 * This way we avoid any flickering
\r
3866 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3867 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3868 boardRect.right - boardRect.left,
\r
3869 boardRect.bottom - boardRect.top,
\r
3870 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3871 if(saveDiagFlag) {
\r
3872 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3873 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3875 GetObject(bufferBitmap, sizeof(b), &b);
\r
3876 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3877 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3878 bih.biWidth = b.bmWidth;
\r
3879 bih.biHeight = b.bmHeight;
\r
3881 bih.biBitCount = b.bmBitsPixel;
\r
3882 bih.biCompression = 0;
\r
3883 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3884 bih.biXPelsPerMeter = 0;
\r
3885 bih.biYPelsPerMeter = 0;
\r
3886 bih.biClrUsed = 0;
\r
3887 bih.biClrImportant = 0;
\r
3888 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3889 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3890 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3891 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3893 wb = b.bmWidthBytes;
\r
3895 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3896 int k = ((int*) pData)[i];
\r
3897 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3898 if(j >= 16) break;
\r
3900 if(j >= nrColors) nrColors = j+1;
\r
3902 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3904 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3905 for(w=0; w<(wb>>2); w+=2) {
\r
3906 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3907 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3908 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3909 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3910 pData[p++] = m | j<<4;
\r
3912 while(p&3) pData[p++] = 0;
\r
3915 wb = ((wb+31)>>5)<<2;
\r
3917 // write BITMAPFILEHEADER
\r
3918 fprintf(diagFile, "BM");
\r
3919 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3920 fputDW(diagFile, 0);
\r
3921 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3922 // write BITMAPINFOHEADER
\r
3923 fputDW(diagFile, 40);
\r
3924 fputDW(diagFile, b.bmWidth);
\r
3925 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3926 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3927 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3928 fputDW(diagFile, 0);
\r
3929 fputDW(diagFile, 0);
\r
3930 fputDW(diagFile, 0);
\r
3931 fputDW(diagFile, 0);
\r
3932 fputDW(diagFile, 0);
\r
3933 fputDW(diagFile, 0);
\r
3934 // write color table
\r
3936 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3937 // write bitmap data
\r
3938 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3939 fputc(pData[i], diagFile);
\r
3944 SelectObject(tmphdc, oldBitmap);
\r
3946 /* Massive cleanup */
\r
3947 for (x = 0; x < num_clips; x++)
\r
3948 DeleteObject(clips[x]);
\r
3951 DeleteObject(bufferBitmap);
\r
3954 ReleaseDC(hwndMain, hdc);
\r
3956 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3958 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3960 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3963 /* CopyBoard(lastDrawn, board);*/
\r
3964 lastDrawnHighlight = highlightInfo;
\r
3965 lastDrawnPremove = premoveHighlightInfo;
\r
3966 lastDrawnFlipView = flipView;
\r
3967 lastDrawnValid[nr] = 1;
\r
3970 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3975 saveDiagFlag = 1; diagFile = f;
\r
3976 HDCDrawPosition(NULL, TRUE, NULL);
\r
3984 /*---------------------------------------------------------------------------*\
\r
3985 | CLIENT PAINT PROCEDURE
\r
3986 | This is the main event-handler for the WM_PAINT message.
\r
3988 \*---------------------------------------------------------------------------*/
\r
3990 PaintProc(HWND hwnd)
\r
3996 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3997 if (IsIconic(hwnd)) {
\r
3998 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4000 if (!appData.monoMode) {
\r
4001 SelectPalette(hdc, hPal, FALSE);
\r
4002 RealizePalette(hdc);
\r
4004 HDCDrawPosition(hdc, 1, NULL);
\r
4005 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4006 flipView = !flipView; partnerUp = !partnerUp;
\r
4007 HDCDrawPosition(hdc, 1, NULL);
\r
4008 flipView = !flipView; partnerUp = !partnerUp;
\r
4011 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4012 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4013 ETO_CLIPPED|ETO_OPAQUE,
\r
4014 &messageRect, messageText, strlen(messageText), NULL);
\r
4015 SelectObject(hdc, oldFont);
\r
4016 DisplayBothClocks();
\r
4019 EndPaint(hwnd,&ps);
\r
4027 * If the user selects on a border boundary, return -1; if off the board,
\r
4028 * return -2. Otherwise map the event coordinate to the square.
\r
4029 * The offset boardRect.left or boardRect.top must already have been
\r
4030 * subtracted from x.
\r
4032 int EventToSquare(x, limit)
\r
4040 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4042 x /= (squareSize + lineGap);
\r
4054 DropEnable dropEnables[] = {
\r
4055 { 'P', DP_Pawn, N_("Pawn") },
\r
4056 { 'N', DP_Knight, N_("Knight") },
\r
4057 { 'B', DP_Bishop, N_("Bishop") },
\r
4058 { 'R', DP_Rook, N_("Rook") },
\r
4059 { 'Q', DP_Queen, N_("Queen") },
\r
4063 SetupDropMenu(HMENU hmenu)
\r
4065 int i, count, enable;
\r
4067 extern char white_holding[], black_holding[];
\r
4068 char item[MSG_SIZ];
\r
4070 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4071 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4072 dropEnables[i].piece);
\r
4074 while (p && *p++ == dropEnables[i].piece) count++;
\r
4075 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4076 enable = count > 0 || !appData.testLegality
\r
4077 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4078 && !appData.icsActive);
\r
4079 ModifyMenu(hmenu, dropEnables[i].command,
\r
4080 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4081 dropEnables[i].command, item);
\r
4085 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4087 dragInfo.lastpos.x = boardRect.left + x;
\r
4088 dragInfo.lastpos.y = boardRect.top + y;
\r
4089 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4090 dragInfo.from.x = fromX;
\r
4091 dragInfo.from.y = fromY;
\r
4092 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4093 dragInfo.start = dragInfo.from;
\r
4094 SetCapture(hwndMain);
\r
4097 void DragPieceEnd(int x, int y)
\r
4100 dragInfo.start.x = dragInfo.start.y = -1;
\r
4101 dragInfo.from = dragInfo.start;
\r
4102 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4105 void ChangeDragPiece(ChessSquare piece)
\r
4107 dragInfo.piece = piece;
\r
4110 /* Event handler for mouse messages */
\r
4112 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4116 static int recursive = 0;
\r
4118 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4121 if (message == WM_MBUTTONUP) {
\r
4122 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4123 to the middle button: we simulate pressing the left button too!
\r
4125 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4126 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4132 pt.x = LOWORD(lParam);
\r
4133 pt.y = HIWORD(lParam);
\r
4134 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4135 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4136 if (!flipView && y >= 0) {
\r
4137 y = BOARD_HEIGHT - 1 - y;
\r
4139 if (flipView && x >= 0) {
\r
4140 x = BOARD_WIDTH - 1 - x;
\r
4143 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4145 switch (message) {
\r
4146 case WM_LBUTTONDOWN:
\r
4147 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4148 ClockClick(flipClock);
\r
4149 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4150 ClockClick(!flipClock);
\r
4152 dragInfo.start.x = dragInfo.start.y = -1;
\r
4153 dragInfo.from = dragInfo.start;
\r
4154 if(fromX == -1 && frozen) { // not sure where this is for
\r
4155 fromX = fromY = -1;
\r
4156 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4159 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4160 DrawPosition(TRUE, NULL);
\r
4163 case WM_LBUTTONUP:
\r
4164 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4165 DrawPosition(TRUE, NULL);
\r
4168 case WM_MOUSEMOVE:
\r
4169 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4170 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4171 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4172 if ((appData.animateDragging || appData.highlightDragging)
\r
4173 && (wParam & MK_LBUTTON)
\r
4174 && dragInfo.from.x >= 0)
\r
4176 BOOL full_repaint = FALSE;
\r
4178 if (appData.animateDragging) {
\r
4179 dragInfo.pos = pt;
\r
4181 if (appData.highlightDragging) {
\r
4182 SetHighlights(fromX, fromY, x, y);
\r
4183 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4184 full_repaint = TRUE;
\r
4188 DrawPosition( full_repaint, NULL);
\r
4190 dragInfo.lastpos = dragInfo.pos;
\r
4194 case WM_MOUSEWHEEL: // [DM]
\r
4195 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4196 /* Mouse Wheel is being rolled forward
\r
4197 * Play moves forward
\r
4199 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4200 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4201 /* Mouse Wheel is being rolled backward
\r
4202 * Play moves backward
\r
4204 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4205 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4209 case WM_MBUTTONUP:
\r
4210 case WM_RBUTTONUP:
\r
4212 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4215 case WM_MBUTTONDOWN:
\r
4216 case WM_RBUTTONDOWN:
\r
4219 fromX = fromY = -1;
\r
4220 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4221 dragInfo.start.x = dragInfo.start.y = -1;
\r
4222 dragInfo.from = dragInfo.start;
\r
4223 dragInfo.lastpos = dragInfo.pos;
\r
4224 if (appData.highlightDragging) {
\r
4225 ClearHighlights();
\r
4228 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4229 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4230 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4231 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4232 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4236 DrawPosition(TRUE, NULL);
\r
4238 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4241 if (message == WM_MBUTTONDOWN) {
\r
4242 buttonCount = 3; /* even if system didn't think so */
\r
4243 if (wParam & MK_SHIFT)
\r
4244 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4246 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4247 } else { /* message == WM_RBUTTONDOWN */
\r
4248 /* Just have one menu, on the right button. Windows users don't
\r
4249 think to try the middle one, and sometimes other software steals
\r
4250 it, or it doesn't really exist. */
\r
4251 if(gameInfo.variant != VariantShogi)
\r
4252 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4254 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4258 SetCapture(hwndMain);
4261 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4262 SetupDropMenu(hmenu);
\r
4263 MenuPopup(hwnd, pt, hmenu, -1);
\r
4273 /* Preprocess messages for buttons in main window */
\r
4275 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4277 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4280 for (i=0; i<N_BUTTONS; i++) {
\r
4281 if (buttonDesc[i].id == id) break;
\r
4283 if (i == N_BUTTONS) return 0;
\r
4284 switch (message) {
\r
4289 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4290 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4297 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4300 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4301 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4302 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4303 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4305 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4307 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4308 TypeInEvent((char)wParam);
\r
4314 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4317 /* Process messages for Promotion dialog box */
\r
4319 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4323 switch (message) {
\r
4324 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4325 /* Center the dialog over the application window */
\r
4326 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4327 Translate(hDlg, DLG_PromotionKing);
\r
4328 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4329 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4330 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4331 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4332 SW_SHOW : SW_HIDE);
\r
4333 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4334 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4335 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4336 PieceToChar(WhiteAngel) != '~') ||
\r
4337 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4338 PieceToChar(BlackAngel) != '~') ) ?
\r
4339 SW_SHOW : SW_HIDE);
\r
4340 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4341 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4342 PieceToChar(WhiteMarshall) != '~') ||
\r
4343 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4344 PieceToChar(BlackMarshall) != '~') ) ?
\r
4345 SW_SHOW : SW_HIDE);
\r
4346 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4347 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4348 gameInfo.variant != VariantShogi ?
\r
4349 SW_SHOW : SW_HIDE);
\r
4350 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4351 gameInfo.variant != VariantShogi ?
\r
4352 SW_SHOW : SW_HIDE);
\r
4353 if(gameInfo.variant == VariantShogi) {
\r
4354 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4355 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4356 SetWindowText(hDlg, "Promote?");
\r
4358 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4359 gameInfo.variant == VariantSuper ?
\r
4360 SW_SHOW : SW_HIDE);
\r
4363 case WM_COMMAND: /* message: received a command */
\r
4364 switch (LOWORD(wParam)) {
\r
4366 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4367 ClearHighlights();
\r
4368 DrawPosition(FALSE, NULL);
\r
4371 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4374 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4377 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4378 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4381 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4382 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4384 case PB_Chancellor:
\r
4385 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4387 case PB_Archbishop:
\r
4388 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4391 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4396 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4397 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4398 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4399 fromX = fromY = -1;
\r
4400 if (!appData.highlightLastMove) {
\r
4401 ClearHighlights();
\r
4402 DrawPosition(FALSE, NULL);
\r
4409 /* Pop up promotion dialog */
\r
4411 PromotionPopup(HWND hwnd)
\r
4415 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4416 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4417 hwnd, (DLGPROC)lpProc);
\r
4418 FreeProcInstance(lpProc);
\r
4424 DrawPosition(TRUE, NULL);
\r
4425 PromotionPopup(hwndMain);
\r
4428 /* Toggle ShowThinking */
\r
4430 ToggleShowThinking()
\r
4432 appData.showThinking = !appData.showThinking;
\r
4433 ShowThinkingEvent();
\r
4437 LoadGameDialog(HWND hwnd, char* title)
\r
4441 char fileTitle[MSG_SIZ];
\r
4442 f = OpenFileDialog(hwnd, "rb", "",
\r
4443 appData.oldSaveStyle ? "gam" : "pgn",
\r
4445 title, &number, fileTitle, NULL);
\r
4447 cmailMsgLoaded = FALSE;
\r
4448 if (number == 0) {
\r
4449 int error = GameListBuild(f);
\r
4451 DisplayError(_("Cannot build game list"), error);
\r
4452 } else if (!ListEmpty(&gameList) &&
\r
4453 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4454 GameListPopUp(f, fileTitle);
\r
4457 GameListDestroy();
\r
4460 LoadGame(f, number, fileTitle, FALSE);
\r
4464 int get_term_width()
\r
4469 HFONT hfont, hold_font;
\r
4474 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4478 // get the text metrics
\r
4479 hdc = GetDC(hText);
\r
4480 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4481 if (consoleCF.dwEffects & CFE_BOLD)
\r
4482 lf.lfWeight = FW_BOLD;
\r
4483 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4484 lf.lfItalic = TRUE;
\r
4485 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4486 lf.lfStrikeOut = TRUE;
\r
4487 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4488 lf.lfUnderline = TRUE;
\r
4489 hfont = CreateFontIndirect(&lf);
\r
4490 hold_font = SelectObject(hdc, hfont);
\r
4491 GetTextMetrics(hdc, &tm);
\r
4492 SelectObject(hdc, hold_font);
\r
4493 DeleteObject(hfont);
\r
4494 ReleaseDC(hText, hdc);
\r
4496 // get the rectangle
\r
4497 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4499 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4502 void UpdateICSWidth(HWND hText)
\r
4504 LONG old_width, new_width;
\r
4506 new_width = get_term_width(hText, FALSE);
\r
4507 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4508 if (new_width != old_width)
\r
4510 ics_update_width(new_width);
\r
4511 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4516 ChangedConsoleFont()
\r
4519 CHARRANGE tmpsel, sel;
\r
4520 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4521 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4522 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4525 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4526 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4527 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4528 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4529 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4530 * size. This was undocumented in the version of MSVC++ that I had
\r
4531 * when I wrote the code, but is apparently documented now.
\r
4533 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4534 cfmt.bCharSet = f->lf.lfCharSet;
\r
4535 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4536 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4537 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4538 /* Why are the following seemingly needed too? */
\r
4539 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4540 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4541 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4543 tmpsel.cpMax = -1; /*999999?*/
\r
4544 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4545 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4546 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4547 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4549 paraf.cbSize = sizeof(paraf);
\r
4550 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4551 paraf.dxStartIndent = 0;
\r
4552 paraf.dxOffset = WRAP_INDENT;
\r
4553 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4554 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4555 UpdateICSWidth(hText);
\r
4558 /*---------------------------------------------------------------------------*\
\r
4560 * Window Proc for main window
\r
4562 \*---------------------------------------------------------------------------*/
\r
4564 /* Process messages for main window, etc. */
\r
4566 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4569 int wmId, wmEvent;
\r
4573 char fileTitle[MSG_SIZ];
\r
4574 char buf[MSG_SIZ];
\r
4575 static SnapData sd;
\r
4577 switch (message) {
\r
4579 case WM_PAINT: /* message: repaint portion of window */
\r
4583 case WM_ERASEBKGND:
\r
4584 if (IsIconic(hwnd)) {
\r
4585 /* Cheat; change the message */
\r
4586 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4588 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4592 case WM_LBUTTONDOWN:
\r
4593 case WM_MBUTTONDOWN:
\r
4594 case WM_RBUTTONDOWN:
\r
4595 case WM_LBUTTONUP:
\r
4596 case WM_MBUTTONUP:
\r
4597 case WM_RBUTTONUP:
\r
4598 case WM_MOUSEMOVE:
\r
4599 case WM_MOUSEWHEEL:
\r
4600 MouseEvent(hwnd, message, wParam, lParam);
\r
4603 JAWS_KB_NAVIGATION
\r
4607 JAWS_ALT_INTERCEPT
\r
4609 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4610 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4611 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4612 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4614 SendMessage(h, message, wParam, lParam);
\r
4615 } else if(lParam != KF_REPEAT) {
\r
4616 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4617 TypeInEvent((char)wParam);
\r
4618 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4619 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4624 case WM_PALETTECHANGED:
\r
4625 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4627 HDC hdc = GetDC(hwndMain);
\r
4628 SelectPalette(hdc, hPal, TRUE);
\r
4629 nnew = RealizePalette(hdc);
\r
4631 paletteChanged = TRUE;
\r
4632 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4634 ReleaseDC(hwnd, hdc);
\r
4638 case WM_QUERYNEWPALETTE:
\r
4639 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4641 HDC hdc = GetDC(hwndMain);
\r
4642 paletteChanged = FALSE;
\r
4643 SelectPalette(hdc, hPal, FALSE);
\r
4644 nnew = RealizePalette(hdc);
\r
4646 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4648 ReleaseDC(hwnd, hdc);
\r
4653 case WM_COMMAND: /* message: command from application menu */
\r
4654 wmId = LOWORD(wParam);
\r
4655 wmEvent = HIWORD(wParam);
\r
4660 SAY("new game enter a move to play against the computer with white");
\r
4663 case IDM_NewGameFRC:
\r
4664 if( NewGameFRC() == 0 ) {
\r
4669 case IDM_NewVariant:
\r
4670 NewVariantPopup(hwnd);
\r
4673 case IDM_LoadGame:
\r
4674 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4677 case IDM_LoadNextGame:
\r
4681 case IDM_LoadPrevGame:
\r
4685 case IDM_ReloadGame:
\r
4689 case IDM_LoadPosition:
\r
4690 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4691 Reset(FALSE, TRUE);
\r
4694 f = OpenFileDialog(hwnd, "rb", "",
\r
4695 appData.oldSaveStyle ? "pos" : "fen",
\r
4697 _("Load Position from File"), &number, fileTitle, NULL);
\r
4699 LoadPosition(f, number, fileTitle);
\r
4703 case IDM_LoadNextPosition:
\r
4704 ReloadPosition(1);
\r
4707 case IDM_LoadPrevPosition:
\r
4708 ReloadPosition(-1);
\r
4711 case IDM_ReloadPosition:
\r
4712 ReloadPosition(0);
\r
4715 case IDM_SaveGame:
\r
4716 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4717 f = OpenFileDialog(hwnd, "a", defName,
\r
4718 appData.oldSaveStyle ? "gam" : "pgn",
\r
4720 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4722 SaveGame(f, 0, "");
\r
4726 case IDM_SavePosition:
\r
4727 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4728 f = OpenFileDialog(hwnd, "a", defName,
\r
4729 appData.oldSaveStyle ? "pos" : "fen",
\r
4731 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4733 SavePosition(f, 0, "");
\r
4737 case IDM_SaveDiagram:
\r
4738 defName = "diagram";
\r
4739 f = OpenFileDialog(hwnd, "wb", defName,
\r
4742 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4748 case IDM_CopyGame:
\r
4749 CopyGameToClipboard();
\r
4752 case IDM_PasteGame:
\r
4753 PasteGameFromClipboard();
\r
4756 case IDM_CopyGameListToClipboard:
\r
4757 CopyGameListToClipboard();
\r
4760 /* [AS] Autodetect FEN or PGN data */
\r
4761 case IDM_PasteAny:
\r
4762 PasteGameOrFENFromClipboard();
\r
4765 /* [AS] Move history */
\r
4766 case IDM_ShowMoveHistory:
\r
4767 if( MoveHistoryIsUp() ) {
\r
4768 MoveHistoryPopDown();
\r
4771 MoveHistoryPopUp();
\r
4775 /* [AS] Eval graph */
\r
4776 case IDM_ShowEvalGraph:
\r
4777 if( EvalGraphIsUp() ) {
\r
4778 EvalGraphPopDown();
\r
4782 SetFocus(hwndMain);
\r
4786 /* [AS] Engine output */
\r
4787 case IDM_ShowEngineOutput:
\r
4788 if( EngineOutputIsUp() ) {
\r
4789 EngineOutputPopDown();
\r
4792 EngineOutputPopUp();
\r
4796 /* [AS] User adjudication */
\r
4797 case IDM_UserAdjudication_White:
\r
4798 UserAdjudicationEvent( +1 );
\r
4801 case IDM_UserAdjudication_Black:
\r
4802 UserAdjudicationEvent( -1 );
\r
4805 case IDM_UserAdjudication_Draw:
\r
4806 UserAdjudicationEvent( 0 );
\r
4809 /* [AS] Game list options dialog */
\r
4810 case IDM_GameListOptions:
\r
4811 GameListOptions();
\r
4818 case IDM_CopyPosition:
\r
4819 CopyFENToClipboard();
\r
4822 case IDM_PastePosition:
\r
4823 PasteFENFromClipboard();
\r
4826 case IDM_MailMove:
\r
4830 case IDM_ReloadCMailMsg:
\r
4831 Reset(TRUE, TRUE);
\r
4832 ReloadCmailMsgEvent(FALSE);
\r
4835 case IDM_Minimize:
\r
4836 ShowWindow(hwnd, SW_MINIMIZE);
\r
4843 case IDM_MachineWhite:
\r
4844 MachineWhiteEvent();
\r
4846 * refresh the tags dialog only if it's visible
\r
4848 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4850 tags = PGNTags(&gameInfo);
\r
4851 TagsPopUp(tags, CmailMsg());
\r
4854 SAY("computer starts playing white");
\r
4857 case IDM_MachineBlack:
\r
4858 MachineBlackEvent();
\r
4860 * refresh the tags dialog only if it's visible
\r
4862 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4864 tags = PGNTags(&gameInfo);
\r
4865 TagsPopUp(tags, CmailMsg());
\r
4868 SAY("computer starts playing black");
\r
4871 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4872 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4875 case IDM_TwoMachines:
\r
4876 TwoMachinesEvent();
\r
4878 * refresh the tags dialog only if it's visible
\r
4880 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4882 tags = PGNTags(&gameInfo);
\r
4883 TagsPopUp(tags, CmailMsg());
\r
4886 SAY("computer starts playing both sides");
\r
4889 case IDM_AnalysisMode:
\r
4890 if (!first.analysisSupport) {
\r
4891 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4892 DisplayError(buf, 0);
\r
4894 SAY("analyzing current position");
\r
4895 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4896 if (appData.icsActive) {
\r
4897 if (gameMode != IcsObserving) {
\r
4898 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4899 DisplayError(buf, 0);
\r
4900 /* secure check */
\r
4901 if (appData.icsEngineAnalyze) {
\r
4902 if (appData.debugMode)
\r
4903 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4904 ExitAnalyzeMode();
\r
4910 /* if enable, user want disable icsEngineAnalyze */
\r
4911 if (appData.icsEngineAnalyze) {
\r
4912 ExitAnalyzeMode();
\r
4916 appData.icsEngineAnalyze = TRUE;
\r
4917 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4920 if (!appData.showThinking) ToggleShowThinking();
\r
4921 AnalyzeModeEvent();
\r
4925 case IDM_AnalyzeFile:
\r
4926 if (!first.analysisSupport) {
\r
4927 char buf[MSG_SIZ];
\r
4928 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4929 DisplayError(buf, 0);
\r
4931 if (!appData.showThinking) ToggleShowThinking();
\r
4932 AnalyzeFileEvent();
\r
4933 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4934 AnalysisPeriodicEvent(1);
\r
4938 case IDM_IcsClient:
\r
4942 case IDM_EditGame:
\r
4943 case IDM_EditGame2:
\r
4948 case IDM_EditPosition:
\r
4949 case IDM_EditPosition2:
\r
4950 EditPositionEvent();
\r
4951 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4954 case IDM_Training:
\r
4958 case IDM_ShowGameList:
\r
4959 ShowGameListProc();
\r
4962 case IDM_EditProgs1:
\r
4963 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4966 case IDM_EditProgs2:
\r
4967 LoadEnginePopUp(hwndMain);
\r
4968 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4971 case IDM_EditServers:
\r
4972 EditTagsPopUp(icsNames, &icsNames);
\r
4975 case IDM_EditTags:
\r
4980 case IDM_EditBook:
\r
4984 case IDM_EditComment:
\r
4986 if (commentUp && editComment) {
\r
4989 EditCommentEvent();
\r
5009 case IDM_CallFlag:
\r
5029 case IDM_StopObserving:
\r
5030 StopObservingEvent();
\r
5033 case IDM_StopExamining:
\r
5034 StopExaminingEvent();
\r
5038 UploadGameEvent();
\r
5041 case IDM_TypeInMove:
\r
5042 TypeInEvent('\000');
\r
5045 case IDM_TypeInName:
\r
5046 PopUpNameDialog('\000');
\r
5049 case IDM_Backward:
\r
5051 SetFocus(hwndMain);
\r
5058 SetFocus(hwndMain);
\r
5063 SetFocus(hwndMain);
\r
5068 SetFocus(hwndMain);
\r
5071 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5072 case OPT_GameListPrev:
\r
5073 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5077 RevertEvent(FALSE);
\r
5080 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5081 RevertEvent(TRUE);
\r
5084 case IDM_TruncateGame:
\r
5085 TruncateGameEvent();
\r
5092 case IDM_RetractMove:
\r
5093 RetractMoveEvent();
\r
5096 case IDM_FlipView:
\r
5097 flipView = !flipView;
\r
5098 DrawPosition(FALSE, NULL);
\r
5101 case IDM_FlipClock:
\r
5102 flipClock = !flipClock;
\r
5103 DisplayBothClocks();
\r
5107 case IDM_MuteSounds:
\r
5108 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5109 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5110 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5113 case IDM_GeneralOptions:
\r
5114 GeneralOptionsPopup(hwnd);
\r
5115 DrawPosition(TRUE, NULL);
\r
5118 case IDM_BoardOptions:
\r
5119 BoardOptionsPopup(hwnd);
\r
5122 case IDM_EnginePlayOptions:
\r
5123 EnginePlayOptionsPopup(hwnd);
\r
5126 case IDM_Engine1Options:
\r
5127 EngineOptionsPopup(hwnd, &first);
\r
5130 case IDM_Engine2Options:
\r
5132 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5133 EngineOptionsPopup(hwnd, &second);
\r
5136 case IDM_OptionsUCI:
\r
5137 UciOptionsPopup(hwnd);
\r
5141 TourneyPopup(hwnd);
\r
5144 case IDM_IcsOptions:
\r
5145 IcsOptionsPopup(hwnd);
\r
5149 FontsOptionsPopup(hwnd);
\r
5153 SoundOptionsPopup(hwnd);
\r
5156 case IDM_CommPort:
\r
5157 CommPortOptionsPopup(hwnd);
\r
5160 case IDM_LoadOptions:
\r
5161 LoadOptionsPopup(hwnd);
\r
5164 case IDM_SaveOptions:
\r
5165 SaveOptionsPopup(hwnd);
\r
5168 case IDM_TimeControl:
\r
5169 TimeControlOptionsPopup(hwnd);
\r
5172 case IDM_SaveSettings:
\r
5173 SaveSettings(settingsFileName);
\r
5176 case IDM_SaveSettingsOnExit:
\r
5177 saveSettingsOnExit = !saveSettingsOnExit;
\r
5178 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5179 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5180 MF_CHECKED : MF_UNCHECKED));
\r
5191 case IDM_AboutGame:
\r
5196 appData.debugMode = !appData.debugMode;
\r
5197 if (appData.debugMode) {
\r
5198 char dir[MSG_SIZ];
\r
5199 GetCurrentDirectory(MSG_SIZ, dir);
\r
5200 SetCurrentDirectory(installDir);
\r
5201 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5202 SetCurrentDirectory(dir);
\r
5203 setbuf(debugFP, NULL);
\r
5210 case IDM_HELPCONTENTS:
\r
5211 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5212 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5213 MessageBox (GetFocus(),
\r
5214 _("Unable to activate help"),
\r
5215 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5219 case IDM_HELPSEARCH:
\r
5220 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\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_HELPHELP:
\r
5229 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5230 MessageBox (GetFocus(),
\r
5231 _("Unable to activate help"),
\r
5232 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5237 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5239 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5240 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5241 FreeProcInstance(lpProc);
\r
5244 case IDM_DirectCommand1:
\r
5245 AskQuestionEvent(_("Direct Command"),
\r
5246 _("Send to chess program:"), "", "1");
\r
5248 case IDM_DirectCommand2:
\r
5249 AskQuestionEvent(_("Direct Command"),
\r
5250 _("Send to second chess program:"), "", "2");
\r
5253 case EP_WhitePawn:
\r
5254 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5255 fromX = fromY = -1;
\r
5258 case EP_WhiteKnight:
\r
5259 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5260 fromX = fromY = -1;
\r
5263 case EP_WhiteBishop:
\r
5264 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5265 fromX = fromY = -1;
\r
5268 case EP_WhiteRook:
\r
5269 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5270 fromX = fromY = -1;
\r
5273 case EP_WhiteQueen:
\r
5274 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5275 fromX = fromY = -1;
\r
5278 case EP_WhiteFerz:
\r
5279 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5280 fromX = fromY = -1;
\r
5283 case EP_WhiteWazir:
\r
5284 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5285 fromX = fromY = -1;
\r
5288 case EP_WhiteAlfil:
\r
5289 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5290 fromX = fromY = -1;
\r
5293 case EP_WhiteCannon:
\r
5294 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5298 case EP_WhiteCardinal:
\r
5299 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5303 case EP_WhiteMarshall:
\r
5304 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5308 case EP_WhiteKing:
\r
5309 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_BlackPawn:
\r
5314 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_BlackKnight:
\r
5319 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_BlackBishop:
\r
5324 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_BlackRook:
\r
5329 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5333 case EP_BlackQueen:
\r
5334 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5338 case EP_BlackFerz:
\r
5339 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5343 case EP_BlackWazir:
\r
5344 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5348 case EP_BlackAlfil:
\r
5349 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5353 case EP_BlackCannon:
\r
5354 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5358 case EP_BlackCardinal:
\r
5359 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5363 case EP_BlackMarshall:
\r
5364 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5368 case EP_BlackKing:
\r
5369 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5373 case EP_EmptySquare:
\r
5374 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5378 case EP_ClearBoard:
\r
5379 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5384 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5389 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5394 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5399 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5404 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5409 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5410 fromX = fromY = -1;
\r
5414 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5415 fromX = fromY = -1;
\r
5419 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5420 fromX = fromY = -1;
\r
5424 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5425 fromX = fromY = -1;
\r
5429 barbaric = 0; appData.language = "";
\r
5430 TranslateMenus(0);
\r
5431 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5432 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5433 lastChecked = wmId;
\r
5437 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5438 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5439 TranslateMenus(0);
\r
5440 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5441 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5442 lastChecked = wmId;
\r
5445 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5451 case CLOCK_TIMER_ID:
\r
5452 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5453 clockTimerEvent = 0;
\r
5454 DecrementClocks(); /* call into back end */
\r
5456 case LOAD_GAME_TIMER_ID:
\r
5457 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5458 loadGameTimerEvent = 0;
\r
5459 AutoPlayGameLoop(); /* call into back end */
\r
5461 case ANALYSIS_TIMER_ID:
\r
5462 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5463 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5464 AnalysisPeriodicEvent(0);
\r
5466 KillTimer(hwnd, analysisTimerEvent);
\r
5467 analysisTimerEvent = 0;
\r
5470 case DELAYED_TIMER_ID:
\r
5471 KillTimer(hwnd, delayedTimerEvent);
\r
5472 delayedTimerEvent = 0;
\r
5473 delayedTimerCallback();
\r
5478 case WM_USER_Input:
\r
5479 InputEvent(hwnd, message, wParam, lParam);
\r
5482 /* [AS] Also move "attached" child windows */
\r
5483 case WM_WINDOWPOSCHANGING:
\r
5485 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5486 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5488 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5489 /* Window is moving */
\r
5492 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5493 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5494 rcMain.right = wpMain.x + wpMain.width;
\r
5495 rcMain.top = wpMain.y;
\r
5496 rcMain.bottom = wpMain.y + wpMain.height;
\r
5498 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5499 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5500 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5501 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5502 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5503 wpMain.x = lpwp->x;
\r
5504 wpMain.y = lpwp->y;
\r
5509 /* [AS] Snapping */
\r
5510 case WM_ENTERSIZEMOVE:
\r
5511 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5512 if (hwnd == hwndMain) {
\r
5513 doingSizing = TRUE;
\r
5516 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5520 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5521 if (hwnd == hwndMain) {
\r
5522 lastSizing = wParam;
\r
5527 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5528 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5530 case WM_EXITSIZEMOVE:
\r
5531 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5532 if (hwnd == hwndMain) {
\r
5534 doingSizing = FALSE;
\r
5535 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5536 GetClientRect(hwnd, &client);
\r
5537 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5539 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5541 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5544 case WM_DESTROY: /* message: window being destroyed */
\r
5545 PostQuitMessage(0);
\r
5549 if (hwnd == hwndMain) {
\r
5554 default: /* Passes it on if unprocessed */
\r
5555 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5560 /*---------------------------------------------------------------------------*\
\r
5562 * Misc utility routines
\r
5564 \*---------------------------------------------------------------------------*/
\r
5567 * Decent random number generator, at least not as bad as Windows
\r
5568 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5570 unsigned int randstate;
\r
5575 randstate = randstate * 1664525 + 1013904223;
\r
5576 return (int) randstate & 0x7fffffff;
\r
5580 mysrandom(unsigned int seed)
\r
5587 * returns TRUE if user selects a different color, FALSE otherwise
\r
5591 ChangeColor(HWND hwnd, COLORREF *which)
\r
5593 static BOOL firstTime = TRUE;
\r
5594 static DWORD customColors[16];
\r
5596 COLORREF newcolor;
\r
5601 /* Make initial colors in use available as custom colors */
\r
5602 /* Should we put the compiled-in defaults here instead? */
\r
5604 customColors[i++] = lightSquareColor & 0xffffff;
\r
5605 customColors[i++] = darkSquareColor & 0xffffff;
\r
5606 customColors[i++] = whitePieceColor & 0xffffff;
\r
5607 customColors[i++] = blackPieceColor & 0xffffff;
\r
5608 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5609 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5611 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5612 customColors[i++] = textAttribs[ccl].color;
\r
5614 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5615 firstTime = FALSE;
\r
5618 cc.lStructSize = sizeof(cc);
\r
5619 cc.hwndOwner = hwnd;
\r
5620 cc.hInstance = NULL;
\r
5621 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5622 cc.lpCustColors = (LPDWORD) customColors;
\r
5623 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5625 if (!ChooseColor(&cc)) return FALSE;
\r
5627 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5628 if (newcolor == *which) return FALSE;
\r
5629 *which = newcolor;
\r
5633 InitDrawingColors();
\r
5634 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5639 MyLoadSound(MySound *ms)
\r
5645 if (ms->data && ms->flag) free(ms->data);
\r
5648 switch (ms->name[0]) {
\r
5654 /* System sound from Control Panel. Don't preload here. */
\r
5658 if (ms->name[1] == NULLCHAR) {
\r
5659 /* "!" alone = silence */
\r
5662 /* Builtin wave resource. Error if not found. */
\r
5663 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5664 if (h == NULL) break;
\r
5665 ms->data = (void *)LoadResource(hInst, h);
\r
5666 ms->flag = 0; // not maloced, so cannot be freed!
\r
5667 if (h == NULL) break;
\r
5672 /* .wav file. Error if not found. */
\r
5673 f = fopen(ms->name, "rb");
\r
5674 if (f == NULL) break;
\r
5675 if (fstat(fileno(f), &st) < 0) break;
\r
5676 ms->data = malloc(st.st_size);
\r
5678 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5684 char buf[MSG_SIZ];
\r
5685 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5686 DisplayError(buf, GetLastError());
\r
5692 MyPlaySound(MySound *ms)
\r
5694 BOOLEAN ok = FALSE;
\r
5696 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5697 switch (ms->name[0]) {
\r
5699 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5704 /* System sound from Control Panel (deprecated feature).
\r
5705 "$" alone or an unset sound name gets default beep (still in use). */
\r
5706 if (ms->name[1]) {
\r
5707 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5709 if (!ok) ok = MessageBeep(MB_OK);
\r
5712 /* Builtin wave resource, or "!" alone for silence */
\r
5713 if (ms->name[1]) {
\r
5714 if (ms->data == NULL) return FALSE;
\r
5715 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5721 /* .wav file. Error if not found. */
\r
5722 if (ms->data == NULL) return FALSE;
\r
5723 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5726 /* Don't print an error: this can happen innocently if the sound driver
\r
5727 is busy; for instance, if another instance of WinBoard is playing
\r
5728 a sound at about the same time. */
\r
5734 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5737 OPENFILENAME *ofn;
\r
5738 static UINT *number; /* gross that this is static */
\r
5740 switch (message) {
\r
5741 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5742 /* Center the dialog over the application window */
\r
5743 ofn = (OPENFILENAME *) lParam;
\r
5744 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5745 number = (UINT *) ofn->lCustData;
\r
5746 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5750 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5751 Translate(hDlg, 1536);
\r
5752 return FALSE; /* Allow for further processing */
\r
5755 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5756 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5758 return FALSE; /* Allow for further processing */
\r
5764 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5766 static UINT *number;
\r
5767 OPENFILENAME *ofname;
\r
5770 case WM_INITDIALOG:
\r
5771 Translate(hdlg, DLG_IndexNumber);
\r
5772 ofname = (OPENFILENAME *)lParam;
\r
5773 number = (UINT *)(ofname->lCustData);
\r
5776 ofnot = (OFNOTIFY *)lParam;
\r
5777 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5778 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5787 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5788 char *nameFilt, char *dlgTitle, UINT *number,
\r
5789 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5791 OPENFILENAME openFileName;
\r
5792 char buf1[MSG_SIZ];
\r
5795 if (fileName == NULL) fileName = buf1;
\r
5796 if (defName == NULL) {
\r
5797 safeStrCpy(fileName, "*.", 3 );
\r
5798 strcat(fileName, defExt);
\r
5800 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5802 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5803 if (number) *number = 0;
\r
5805 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5806 openFileName.hwndOwner = hwnd;
\r
5807 openFileName.hInstance = (HANDLE) hInst;
\r
5808 openFileName.lpstrFilter = nameFilt;
\r
5809 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5810 openFileName.nMaxCustFilter = 0L;
\r
5811 openFileName.nFilterIndex = 1L;
\r
5812 openFileName.lpstrFile = fileName;
\r
5813 openFileName.nMaxFile = MSG_SIZ;
\r
5814 openFileName.lpstrFileTitle = fileTitle;
\r
5815 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5816 openFileName.lpstrInitialDir = NULL;
\r
5817 openFileName.lpstrTitle = dlgTitle;
\r
5818 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5819 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5820 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5821 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5822 openFileName.nFileOffset = 0;
\r
5823 openFileName.nFileExtension = 0;
\r
5824 openFileName.lpstrDefExt = defExt;
\r
5825 openFileName.lCustData = (LONG) number;
\r
5826 openFileName.lpfnHook = oldDialog ?
\r
5827 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5828 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5830 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5831 GetOpenFileName(&openFileName)) {
\r
5832 /* open the file */
\r
5833 f = fopen(openFileName.lpstrFile, write);
\r
5835 MessageBox(hwnd, _("File open failed"), NULL,
\r
5836 MB_OK|MB_ICONEXCLAMATION);
\r
5840 int err = CommDlgExtendedError();
\r
5841 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5850 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5852 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5855 * Get the first pop-up menu in the menu template. This is the
\r
5856 * menu that TrackPopupMenu displays.
\r
5858 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5859 TranslateOneMenu(10, hmenuTrackPopup);
\r
5861 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5864 * TrackPopup uses screen coordinates, so convert the
\r
5865 * coordinates of the mouse click to screen coordinates.
\r
5867 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5869 /* Draw and track the floating pop-up menu. */
\r
5870 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5871 pt.x, pt.y, 0, hwnd, NULL);
\r
5873 /* Destroy the menu.*/
\r
5874 DestroyMenu(hmenu);
\r
5879 int sizeX, sizeY, newSizeX, newSizeY;
\r
5881 } ResizeEditPlusButtonsClosure;
\r
5884 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5886 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5890 if (hChild == cl->hText) return TRUE;
\r
5891 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5892 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5893 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5894 ScreenToClient(cl->hDlg, &pt);
\r
5895 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5896 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5900 /* Resize a dialog that has a (rich) edit field filling most of
\r
5901 the top, with a row of buttons below */
\r
5903 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5906 int newTextHeight, newTextWidth;
\r
5907 ResizeEditPlusButtonsClosure cl;
\r
5909 /*if (IsIconic(hDlg)) return;*/
\r
5910 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5912 cl.hdwp = BeginDeferWindowPos(8);
\r
5914 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5915 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5916 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5917 if (newTextHeight < 0) {
\r
5918 newSizeY += -newTextHeight;
\r
5919 newTextHeight = 0;
\r
5921 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5922 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5928 cl.newSizeX = newSizeX;
\r
5929 cl.newSizeY = newSizeY;
\r
5930 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5932 EndDeferWindowPos(cl.hdwp);
\r
5935 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5937 RECT rChild, rParent;
\r
5938 int wChild, hChild, wParent, hParent;
\r
5939 int wScreen, hScreen, xNew, yNew;
\r
5942 /* Get the Height and Width of the child window */
\r
5943 GetWindowRect (hwndChild, &rChild);
\r
5944 wChild = rChild.right - rChild.left;
\r
5945 hChild = rChild.bottom - rChild.top;
\r
5947 /* Get the Height and Width of the parent window */
\r
5948 GetWindowRect (hwndParent, &rParent);
\r
5949 wParent = rParent.right - rParent.left;
\r
5950 hParent = rParent.bottom - rParent.top;
\r
5952 /* Get the display limits */
\r
5953 hdc = GetDC (hwndChild);
\r
5954 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5955 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5956 ReleaseDC(hwndChild, hdc);
\r
5958 /* Calculate new X position, then adjust for screen */
\r
5959 xNew = rParent.left + ((wParent - wChild) /2);
\r
5962 } else if ((xNew+wChild) > wScreen) {
\r
5963 xNew = wScreen - wChild;
\r
5966 /* Calculate new Y position, then adjust for screen */
\r
5968 yNew = rParent.top + ((hParent - hChild) /2);
\r
5971 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5976 } else if ((yNew+hChild) > hScreen) {
\r
5977 yNew = hScreen - hChild;
\r
5980 /* Set it, and return */
\r
5981 return SetWindowPos (hwndChild, NULL,
\r
5982 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5985 /* Center one window over another */
\r
5986 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5988 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5991 /*---------------------------------------------------------------------------*\
\r
5993 * Startup Dialog functions
\r
5995 \*---------------------------------------------------------------------------*/
\r
5997 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5999 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6001 while (*cd != NULL) {
\r
6002 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6008 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6010 char buf1[MAX_ARG_LEN];
\r
6013 if (str[0] == '@') {
\r
6014 FILE* f = fopen(str + 1, "r");
\r
6016 DisplayFatalError(str + 1, errno, 2);
\r
6019 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6021 buf1[len] = NULLCHAR;
\r
6025 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6028 char buf[MSG_SIZ];
\r
6029 char *end = strchr(str, '\n');
\r
6030 if (end == NULL) return;
\r
6031 memcpy(buf, str, end - str);
\r
6032 buf[end - str] = NULLCHAR;
\r
6033 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6039 SetStartupDialogEnables(HWND hDlg)
\r
6041 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6042 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6043 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6044 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6045 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6046 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6047 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6048 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6049 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6050 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6051 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6052 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6053 IsDlgButtonChecked(hDlg, OPT_View));
\r
6057 QuoteForFilename(char *filename)
\r
6059 int dquote, space;
\r
6060 dquote = strchr(filename, '"') != NULL;
\r
6061 space = strchr(filename, ' ') != NULL;
\r
6062 if (dquote || space) {
\r
6074 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6076 char buf[MSG_SIZ];
\r
6079 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6080 q = QuoteForFilename(nthcp);
\r
6081 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6082 if (*nthdir != NULLCHAR) {
\r
6083 q = QuoteForFilename(nthdir);
\r
6084 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6086 if (*nthcp == NULLCHAR) {
\r
6087 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6088 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6089 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6090 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6095 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6097 char buf[MSG_SIZ];
\r
6101 switch (message) {
\r
6102 case WM_INITDIALOG:
\r
6103 /* Center the dialog */
\r
6104 CenterWindow (hDlg, GetDesktopWindow());
\r
6105 Translate(hDlg, DLG_Startup);
\r
6106 /* Initialize the dialog items */
\r
6107 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6108 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6109 firstChessProgramNames);
\r
6110 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6111 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6112 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6113 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6114 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6115 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6116 if (*appData.icsHelper != NULLCHAR) {
\r
6117 char *q = QuoteForFilename(appData.icsHelper);
\r
6118 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6120 if (*appData.icsHost == NULLCHAR) {
\r
6121 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6122 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6123 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6124 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6125 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6128 if (appData.icsActive) {
\r
6129 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6131 else if (appData.noChessProgram) {
\r
6132 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6135 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6138 SetStartupDialogEnables(hDlg);
\r
6142 switch (LOWORD(wParam)) {
\r
6144 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6145 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6146 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6148 ParseArgs(StringGet, &p);
\r
6149 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6150 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6152 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6153 ParseArgs(StringGet, &p);
\r
6154 SwapEngines(singleList); // ... and then make it 'second'
\r
6155 appData.noChessProgram = FALSE;
\r
6156 appData.icsActive = FALSE;
\r
6157 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6158 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6159 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6161 ParseArgs(StringGet, &p);
\r
6162 if (appData.zippyPlay) {
\r
6163 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6164 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6166 ParseArgs(StringGet, &p);
\r
6168 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6169 appData.noChessProgram = TRUE;
\r
6170 appData.icsActive = FALSE;
\r
6172 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6173 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6176 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6177 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6179 ParseArgs(StringGet, &p);
\r
6181 EndDialog(hDlg, TRUE);
\r
6188 case IDM_HELPCONTENTS:
\r
6189 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6190 MessageBox (GetFocus(),
\r
6191 _("Unable to activate help"),
\r
6192 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6197 SetStartupDialogEnables(hDlg);
\r
6205 /*---------------------------------------------------------------------------*\
\r
6207 * About box dialog functions
\r
6209 \*---------------------------------------------------------------------------*/
\r
6211 /* Process messages for "About" dialog box */
\r
6213 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6215 switch (message) {
\r
6216 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6217 /* Center the dialog over the application window */
\r
6218 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6219 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6220 Translate(hDlg, ABOUTBOX);
\r
6224 case WM_COMMAND: /* message: received a command */
\r
6225 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6226 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6227 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6235 /*---------------------------------------------------------------------------*\
\r
6237 * Comment Dialog functions
\r
6239 \*---------------------------------------------------------------------------*/
\r
6242 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6244 static HANDLE hwndText = NULL;
\r
6245 int len, newSizeX, newSizeY, flags;
\r
6246 static int sizeX, sizeY;
\r
6251 switch (message) {
\r
6252 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6253 /* Initialize the dialog items */
\r
6254 Translate(hDlg, DLG_EditComment);
\r
6255 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6256 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6257 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6258 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6259 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6260 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6261 SetWindowText(hDlg, commentTitle);
\r
6262 if (editComment) {
\r
6263 SetFocus(hwndText);
\r
6265 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6267 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6268 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6269 MAKELPARAM(FALSE, 0));
\r
6270 /* Size and position the dialog */
\r
6271 if (!commentDialog) {
\r
6272 commentDialog = hDlg;
\r
6273 flags = SWP_NOZORDER;
\r
6274 GetClientRect(hDlg, &rect);
\r
6275 sizeX = rect.right;
\r
6276 sizeY = rect.bottom;
\r
6277 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6278 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6279 WINDOWPLACEMENT wp;
\r
6280 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6281 wp.length = sizeof(WINDOWPLACEMENT);
\r
6283 wp.showCmd = SW_SHOW;
\r
6284 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6285 wp.rcNormalPosition.left = wpComment.x;
\r
6286 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6287 wp.rcNormalPosition.top = wpComment.y;
\r
6288 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6289 SetWindowPlacement(hDlg, &wp);
\r
6291 GetClientRect(hDlg, &rect);
\r
6292 newSizeX = rect.right;
\r
6293 newSizeY = rect.bottom;
\r
6294 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6295 newSizeX, newSizeY);
\r
6300 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6303 case WM_COMMAND: /* message: received a command */
\r
6304 switch (LOWORD(wParam)) {
\r
6306 if (editComment) {
\r
6308 /* Read changed options from the dialog box */
\r
6309 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6310 len = GetWindowTextLength(hwndText);
\r
6311 str = (char *) malloc(len + 1);
\r
6312 GetWindowText(hwndText, str, len + 1);
\r
6321 ReplaceComment(commentIndex, str);
\r
6328 case OPT_CancelComment:
\r
6332 case OPT_ClearComment:
\r
6333 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6336 case OPT_EditComment:
\r
6337 EditCommentEvent();
\r
6345 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6346 if( wParam == OPT_CommentText ) {
\r
6347 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6349 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6350 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6354 pt.x = LOWORD( lpMF->lParam );
\r
6355 pt.y = HIWORD( lpMF->lParam );
\r
6357 if(lpMF->msg == WM_CHAR) {
\r
6359 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6360 index = sel.cpMin;
\r
6362 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6364 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6365 len = GetWindowTextLength(hwndText);
\r
6366 str = (char *) malloc(len + 1);
\r
6367 GetWindowText(hwndText, str, len + 1);
\r
6368 ReplaceComment(commentIndex, str);
\r
6369 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6370 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6373 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6374 lpMF->msg = WM_USER;
\r
6382 newSizeX = LOWORD(lParam);
\r
6383 newSizeY = HIWORD(lParam);
\r
6384 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6389 case WM_GETMINMAXINFO:
\r
6390 /* Prevent resizing window too small */
\r
6391 mmi = (MINMAXINFO *) lParam;
\r
6392 mmi->ptMinTrackSize.x = 100;
\r
6393 mmi->ptMinTrackSize.y = 100;
\r
6400 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6405 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6407 if (str == NULL) str = "";
\r
6408 p = (char *) malloc(2 * strlen(str) + 2);
\r
6411 if (*str == '\n') *q++ = '\r';
\r
6415 if (commentText != NULL) free(commentText);
\r
6417 commentIndex = index;
\r
6418 commentTitle = title;
\r
6420 editComment = edit;
\r
6422 if (commentDialog) {
\r
6423 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6424 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6426 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6427 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6428 hwndMain, (DLGPROC)lpProc);
\r
6429 FreeProcInstance(lpProc);
\r
6435 /*---------------------------------------------------------------------------*\
\r
6437 * Type-in move dialog functions
\r
6439 \*---------------------------------------------------------------------------*/
\r
6442 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6444 char move[MSG_SIZ];
\r
6447 switch (message) {
\r
6448 case WM_INITDIALOG:
\r
6449 move[0] = (char) lParam;
\r
6450 move[1] = NULLCHAR;
\r
6451 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6452 Translate(hDlg, DLG_TypeInMove);
\r
6453 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6454 SetWindowText(hInput, move);
\r
6456 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6460 switch (LOWORD(wParam)) {
\r
6463 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6464 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6465 TypeInDoneEvent(move);
\r
6466 EndDialog(hDlg, TRUE);
\r
6469 EndDialog(hDlg, FALSE);
\r
6480 PopUpMoveDialog(char firstchar)
\r
6484 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6485 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6486 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6487 FreeProcInstance(lpProc);
\r
6490 /*---------------------------------------------------------------------------*\
\r
6492 * Type-in name dialog functions
\r
6494 \*---------------------------------------------------------------------------*/
\r
6497 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6499 char move[MSG_SIZ];
\r
6502 switch (message) {
\r
6503 case WM_INITDIALOG:
\r
6504 move[0] = (char) lParam;
\r
6505 move[1] = NULLCHAR;
\r
6506 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6507 Translate(hDlg, DLG_TypeInName);
\r
6508 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6509 SetWindowText(hInput, move);
\r
6511 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6515 switch (LOWORD(wParam)) {
\r
6517 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6518 appData.userName = strdup(move);
\r
6521 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6522 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6523 DisplayTitle(move);
\r
6527 EndDialog(hDlg, TRUE);
\r
6530 EndDialog(hDlg, FALSE);
\r
6541 PopUpNameDialog(char firstchar)
\r
6545 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6546 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6547 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6548 FreeProcInstance(lpProc);
\r
6551 /*---------------------------------------------------------------------------*\
\r
6555 \*---------------------------------------------------------------------------*/
\r
6557 /* Nonmodal error box */
\r
6558 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6559 WPARAM wParam, LPARAM lParam);
\r
6562 ErrorPopUp(char *title, char *content)
\r
6566 BOOLEAN modal = hwndMain == NULL;
\r
6584 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6585 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6588 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6590 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6591 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6592 hwndMain, (DLGPROC)lpProc);
\r
6593 FreeProcInstance(lpProc);
\r
6600 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6601 if (errorDialog == NULL) return;
\r
6602 DestroyWindow(errorDialog);
\r
6603 errorDialog = NULL;
\r
6604 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6608 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6613 switch (message) {
\r
6614 case WM_INITDIALOG:
\r
6615 GetWindowRect(hDlg, &rChild);
\r
6618 SetWindowPos(hDlg, NULL, rChild.left,
\r
6619 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6620 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6624 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6625 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6626 and it doesn't work when you resize the dialog.
\r
6627 For now, just give it a default position.
\r
6629 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6630 Translate(hDlg, DLG_Error);
\r
6632 errorDialog = hDlg;
\r
6633 SetWindowText(hDlg, errorTitle);
\r
6634 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6635 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6639 switch (LOWORD(wParam)) {
\r
6642 if (errorDialog == hDlg) errorDialog = NULL;
\r
6643 DestroyWindow(hDlg);
\r
6655 HWND gothicDialog = NULL;
\r
6658 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6662 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6664 switch (message) {
\r
6665 case WM_INITDIALOG:
\r
6666 GetWindowRect(hDlg, &rChild);
\r
6668 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6672 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6673 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6674 and it doesn't work when you resize the dialog.
\r
6675 For now, just give it a default position.
\r
6677 gothicDialog = hDlg;
\r
6678 SetWindowText(hDlg, errorTitle);
\r
6679 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6680 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6684 switch (LOWORD(wParam)) {
\r
6687 if (errorDialog == hDlg) errorDialog = NULL;
\r
6688 DestroyWindow(hDlg);
\r
6700 GothicPopUp(char *title, VariantClass variant)
\r
6703 static char *lastTitle;
\r
6705 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6706 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6708 if(lastTitle != title && gothicDialog != NULL) {
\r
6709 DestroyWindow(gothicDialog);
\r
6710 gothicDialog = NULL;
\r
6712 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6713 title = lastTitle;
\r
6714 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6715 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6716 hwndMain, (DLGPROC)lpProc);
\r
6717 FreeProcInstance(lpProc);
\r
6722 /*---------------------------------------------------------------------------*\
\r
6724 * Ics Interaction console functions
\r
6726 \*---------------------------------------------------------------------------*/
\r
6728 #define HISTORY_SIZE 64
\r
6729 static char *history[HISTORY_SIZE];
\r
6730 int histIn = 0, histP = 0;
\r
6733 SaveInHistory(char *cmd)
\r
6735 if (history[histIn] != NULL) {
\r
6736 free(history[histIn]);
\r
6737 history[histIn] = NULL;
\r
6739 if (*cmd == NULLCHAR) return;
\r
6740 history[histIn] = StrSave(cmd);
\r
6741 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6742 if (history[histIn] != NULL) {
\r
6743 free(history[histIn]);
\r
6744 history[histIn] = NULL;
\r
6750 PrevInHistory(char *cmd)
\r
6753 if (histP == histIn) {
\r
6754 if (history[histIn] != NULL) free(history[histIn]);
\r
6755 history[histIn] = StrSave(cmd);
\r
6757 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6758 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6760 return history[histP];
\r
6766 if (histP == histIn) return NULL;
\r
6767 histP = (histP + 1) % HISTORY_SIZE;
\r
6768 return history[histP];
\r
6772 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6776 hmenu = LoadMenu(hInst, "TextMenu");
\r
6777 h = GetSubMenu(hmenu, 0);
\r
6779 if (strcmp(e->item, "-") == 0) {
\r
6780 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6781 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6782 int flags = MF_STRING, j = 0;
\r
6783 if (e->item[0] == '|') {
\r
6784 flags |= MF_MENUBARBREAK;
\r
6787 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6788 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6796 WNDPROC consoleTextWindowProc;
\r
6799 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6801 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6802 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6806 SetWindowText(hInput, command);
\r
6808 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6810 sel.cpMin = 999999;
\r
6811 sel.cpMax = 999999;
\r
6812 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6817 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6818 if (sel.cpMin == sel.cpMax) {
\r
6819 /* Expand to surrounding word */
\r
6822 tr.chrg.cpMax = sel.cpMin;
\r
6823 tr.chrg.cpMin = --sel.cpMin;
\r
6824 if (sel.cpMin < 0) break;
\r
6825 tr.lpstrText = name;
\r
6826 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6827 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6831 tr.chrg.cpMin = sel.cpMax;
\r
6832 tr.chrg.cpMax = ++sel.cpMax;
\r
6833 tr.lpstrText = name;
\r
6834 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6835 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6838 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6839 MessageBeep(MB_ICONEXCLAMATION);
\r
6843 tr.lpstrText = name;
\r
6844 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6846 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6847 MessageBeep(MB_ICONEXCLAMATION);
\r
6850 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6853 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6854 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6855 SetWindowText(hInput, buf);
\r
6856 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6858 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6859 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6860 SetWindowText(hInput, buf);
\r
6861 sel.cpMin = 999999;
\r
6862 sel.cpMax = 999999;
\r
6863 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6869 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6874 switch (message) {
\r
6876 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6877 if(wParam=='R') return 0;
\r
6880 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6883 sel.cpMin = 999999;
\r
6884 sel.cpMax = 999999;
\r
6885 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6886 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6891 if(wParam != '\022') {
\r
6892 if (wParam == '\t') {
\r
6893 if (GetKeyState(VK_SHIFT) < 0) {
\r
6895 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6896 if (buttonDesc[0].hwnd) {
\r
6897 SetFocus(buttonDesc[0].hwnd);
\r
6899 SetFocus(hwndMain);
\r
6903 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6906 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6907 JAWS_DELETE( SetFocus(hInput); )
\r
6908 SendMessage(hInput, message, wParam, lParam);
\r
6911 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6913 case WM_RBUTTONDOWN:
\r
6914 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6915 /* Move selection here if it was empty */
\r
6917 pt.x = LOWORD(lParam);
\r
6918 pt.y = HIWORD(lParam);
\r
6919 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6920 if (sel.cpMin == sel.cpMax) {
\r
6921 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6922 sel.cpMax = sel.cpMin;
\r
6923 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6925 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6926 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6928 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6929 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6930 if (sel.cpMin == sel.cpMax) {
\r
6931 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6932 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6934 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6935 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6937 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6938 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6939 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6940 MenuPopup(hwnd, pt, hmenu, -1);
\r
6944 case WM_RBUTTONUP:
\r
6945 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6946 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6947 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6951 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6953 return SendMessage(hInput, message, wParam, lParam);
\r
6954 case WM_MBUTTONDOWN:
\r
6955 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6957 switch (LOWORD(wParam)) {
\r
6958 case IDM_QuickPaste:
\r
6960 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6961 if (sel.cpMin == sel.cpMax) {
\r
6962 MessageBeep(MB_ICONEXCLAMATION);
\r
6965 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6966 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6967 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6972 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6975 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6978 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6982 int i = LOWORD(wParam) - IDM_CommandX;
\r
6983 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6984 icsTextMenuEntry[i].command != NULL) {
\r
6985 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6986 icsTextMenuEntry[i].getname,
\r
6987 icsTextMenuEntry[i].immediate);
\r
6995 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6998 WNDPROC consoleInputWindowProc;
\r
7001 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7003 char buf[MSG_SIZ];
\r
7005 static BOOL sendNextChar = FALSE;
\r
7006 static BOOL quoteNextChar = FALSE;
\r
7007 InputSource *is = consoleInputSource;
\r
7011 switch (message) {
\r
7013 if (!appData.localLineEditing || sendNextChar) {
\r
7014 is->buf[0] = (CHAR) wParam;
\r
7016 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7017 sendNextChar = FALSE;
\r
7020 if (quoteNextChar) {
\r
7021 buf[0] = (char) wParam;
\r
7022 buf[1] = NULLCHAR;
\r
7023 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7024 quoteNextChar = FALSE;
\r
7028 case '\r': /* Enter key */
\r
7029 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7030 if (consoleEcho) SaveInHistory(is->buf);
\r
7031 is->buf[is->count++] = '\n';
\r
7032 is->buf[is->count] = NULLCHAR;
\r
7033 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7034 if (consoleEcho) {
\r
7035 ConsoleOutput(is->buf, is->count, TRUE);
\r
7036 } else if (appData.localLineEditing) {
\r
7037 ConsoleOutput("\n", 1, TRUE);
\r
7040 case '\033': /* Escape key */
\r
7041 SetWindowText(hwnd, "");
\r
7042 cf.cbSize = sizeof(CHARFORMAT);
\r
7043 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7044 if (consoleEcho) {
\r
7045 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7047 cf.crTextColor = COLOR_ECHOOFF;
\r
7049 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7050 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7052 case '\t': /* Tab key */
\r
7053 if (GetKeyState(VK_SHIFT) < 0) {
\r
7055 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7058 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7059 if (buttonDesc[0].hwnd) {
\r
7060 SetFocus(buttonDesc[0].hwnd);
\r
7062 SetFocus(hwndMain);
\r
7066 case '\023': /* Ctrl+S */
\r
7067 sendNextChar = TRUE;
\r
7069 case '\021': /* Ctrl+Q */
\r
7070 quoteNextChar = TRUE;
\r
7080 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7081 p = PrevInHistory(buf);
\r
7083 SetWindowText(hwnd, p);
\r
7084 sel.cpMin = 999999;
\r
7085 sel.cpMax = 999999;
\r
7086 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7091 p = NextInHistory();
\r
7093 SetWindowText(hwnd, p);
\r
7094 sel.cpMin = 999999;
\r
7095 sel.cpMax = 999999;
\r
7096 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7102 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7106 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7110 case WM_MBUTTONDOWN:
\r
7111 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7112 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7114 case WM_RBUTTONUP:
\r
7115 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7116 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7117 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7121 hmenu = LoadMenu(hInst, "InputMenu");
\r
7122 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7123 if (sel.cpMin == sel.cpMax) {
\r
7124 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7125 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7127 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7128 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7130 pt.x = LOWORD(lParam);
\r
7131 pt.y = HIWORD(lParam);
\r
7132 MenuPopup(hwnd, pt, hmenu, -1);
\r
7136 switch (LOWORD(wParam)) {
\r
7138 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7140 case IDM_SelectAll:
\r
7142 sel.cpMax = -1; /*999999?*/
\r
7143 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7146 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7149 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7152 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7157 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7160 #define CO_MAX 100000
\r
7161 #define CO_TRIM 1000
\r
7164 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7166 static SnapData sd;
\r
7167 HWND hText, hInput;
\r
7169 static int sizeX, sizeY;
\r
7170 int newSizeX, newSizeY;
\r
7174 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7175 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7177 switch (message) {
\r
7179 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7181 ENLINK *pLink = (ENLINK*)lParam;
\r
7182 if (pLink->msg == WM_LBUTTONUP)
\r
7186 tr.chrg = pLink->chrg;
\r
7187 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7188 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7189 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7190 free(tr.lpstrText);
\r
7194 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7195 hwndConsole = hDlg;
\r
7197 consoleTextWindowProc = (WNDPROC)
\r
7198 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7199 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7200 consoleInputWindowProc = (WNDPROC)
\r
7201 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7202 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7203 Colorize(ColorNormal, TRUE);
\r
7204 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7205 ChangedConsoleFont();
\r
7206 GetClientRect(hDlg, &rect);
\r
7207 sizeX = rect.right;
\r
7208 sizeY = rect.bottom;
\r
7209 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7210 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7211 WINDOWPLACEMENT wp;
\r
7212 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7213 wp.length = sizeof(WINDOWPLACEMENT);
\r
7215 wp.showCmd = SW_SHOW;
\r
7216 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7217 wp.rcNormalPosition.left = wpConsole.x;
\r
7218 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7219 wp.rcNormalPosition.top = wpConsole.y;
\r
7220 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7221 SetWindowPlacement(hDlg, &wp);
\r
7224 // [HGM] Chessknight's change 2004-07-13
\r
7225 else { /* Determine Defaults */
\r
7226 WINDOWPLACEMENT wp;
\r
7227 wpConsole.x = wpMain.width + 1;
\r
7228 wpConsole.y = wpMain.y;
\r
7229 wpConsole.width = screenWidth - wpMain.width;
\r
7230 wpConsole.height = wpMain.height;
\r
7231 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7232 wp.length = sizeof(WINDOWPLACEMENT);
\r
7234 wp.showCmd = SW_SHOW;
\r
7235 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7236 wp.rcNormalPosition.left = wpConsole.x;
\r
7237 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7238 wp.rcNormalPosition.top = wpConsole.y;
\r
7239 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7240 SetWindowPlacement(hDlg, &wp);
\r
7243 // Allow hText to highlight URLs and send notifications on them
\r
7244 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7245 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7246 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7247 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7261 if (IsIconic(hDlg)) break;
\r
7262 newSizeX = LOWORD(lParam);
\r
7263 newSizeY = HIWORD(lParam);
\r
7264 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7265 RECT rectText, rectInput;
\r
7267 int newTextHeight, newTextWidth;
\r
7268 GetWindowRect(hText, &rectText);
\r
7269 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7270 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7271 if (newTextHeight < 0) {
\r
7272 newSizeY += -newTextHeight;
\r
7273 newTextHeight = 0;
\r
7275 SetWindowPos(hText, NULL, 0, 0,
\r
7276 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7277 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7278 pt.x = rectInput.left;
\r
7279 pt.y = rectInput.top + newSizeY - sizeY;
\r
7280 ScreenToClient(hDlg, &pt);
\r
7281 SetWindowPos(hInput, NULL,
\r
7282 pt.x, pt.y, /* needs client coords */
\r
7283 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7284 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7290 case WM_GETMINMAXINFO:
\r
7291 /* Prevent resizing window too small */
\r
7292 mmi = (MINMAXINFO *) lParam;
\r
7293 mmi->ptMinTrackSize.x = 100;
\r
7294 mmi->ptMinTrackSize.y = 100;
\r
7297 /* [AS] Snapping */
\r
7298 case WM_ENTERSIZEMOVE:
\r
7299 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7302 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7305 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7307 case WM_EXITSIZEMOVE:
\r
7308 UpdateICSWidth(hText);
\r
7309 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7312 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7320 if (hwndConsole) return;
\r
7321 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7322 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7327 ConsoleOutput(char* data, int length, int forceVisible)
\r
7332 char buf[CO_MAX+1];
\r
7335 static int delayLF = 0;
\r
7336 CHARRANGE savesel, sel;
\r
7338 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7346 while (length--) {
\r
7354 } else if (*p == '\007') {
\r
7355 MyPlaySound(&sounds[(int)SoundBell]);
\r
7362 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7363 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7364 /* Save current selection */
\r
7365 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7366 exlen = GetWindowTextLength(hText);
\r
7367 /* Find out whether current end of text is visible */
\r
7368 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7369 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7370 /* Trim existing text if it's too long */
\r
7371 if (exlen + (q - buf) > CO_MAX) {
\r
7372 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7375 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7376 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7378 savesel.cpMin -= trim;
\r
7379 savesel.cpMax -= trim;
\r
7380 if (exlen < 0) exlen = 0;
\r
7381 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7382 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7384 /* Append the new text */
\r
7385 sel.cpMin = exlen;
\r
7386 sel.cpMax = exlen;
\r
7387 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7388 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7389 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7390 if (forceVisible || exlen == 0 ||
\r
7391 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7392 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7393 /* Scroll to make new end of text visible if old end of text
\r
7394 was visible or new text is an echo of user typein */
\r
7395 sel.cpMin = 9999999;
\r
7396 sel.cpMax = 9999999;
\r
7397 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7398 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7399 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7400 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7402 if (savesel.cpMax == exlen || forceVisible) {
\r
7403 /* Move insert point to new end of text if it was at the old
\r
7404 end of text or if the new text is an echo of user typein */
\r
7405 sel.cpMin = 9999999;
\r
7406 sel.cpMax = 9999999;
\r
7407 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7409 /* Restore previous selection */
\r
7410 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7412 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7419 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7423 COLORREF oldFg, oldBg;
\r
7428 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7430 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7431 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7432 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7435 rect.right = x + squareSize;
\r
7437 rect.bottom = y + squareSize;
\r
7440 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7441 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7442 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7443 &rect, str, strlen(str), NULL);
\r
7445 (void) SetTextColor(hdc, oldFg);
\r
7446 (void) SetBkColor(hdc, oldBg);
\r
7447 (void) SelectObject(hdc, oldFont);
\r
7451 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7452 RECT *rect, char *color, char *flagFell)
\r
7456 COLORREF oldFg, oldBg;
\r
7459 if (appData.clockMode) {
\r
7461 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7463 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7470 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7471 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7473 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7474 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7476 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7480 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7481 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7482 rect, str, strlen(str), NULL);
\r
7483 if(logoHeight > 0 && appData.clockMode) {
\r
7485 str += strlen(color)+2;
\r
7486 r.top = rect->top + logoHeight/2;
\r
7487 r.left = rect->left;
\r
7488 r.right = rect->right;
\r
7489 r.bottom = rect->bottom;
\r
7490 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7491 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7492 &r, str, strlen(str), NULL);
\r
7494 (void) SetTextColor(hdc, oldFg);
\r
7495 (void) SetBkColor(hdc, oldBg);
\r
7496 (void) SelectObject(hdc, oldFont);
\r
7501 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7507 if( count <= 0 ) {
\r
7508 if (appData.debugMode) {
\r
7509 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7512 return ERROR_INVALID_USER_BUFFER;
\r
7515 ResetEvent(ovl->hEvent);
\r
7516 ovl->Offset = ovl->OffsetHigh = 0;
\r
7517 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7521 err = GetLastError();
\r
7522 if (err == ERROR_IO_PENDING) {
\r
7523 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7527 err = GetLastError();
\r
7534 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7539 ResetEvent(ovl->hEvent);
\r
7540 ovl->Offset = ovl->OffsetHigh = 0;
\r
7541 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7545 err = GetLastError();
\r
7546 if (err == ERROR_IO_PENDING) {
\r
7547 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7551 err = GetLastError();
\r
7557 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7558 void CheckForInputBufferFull( InputSource * is )
\r
7560 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7561 /* Look for end of line */
\r
7562 char * p = is->buf;
\r
7564 while( p < is->next && *p != '\n' ) {
\r
7568 if( p >= is->next ) {
\r
7569 if (appData.debugMode) {
\r
7570 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7573 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7574 is->count = (DWORD) -1;
\r
7575 is->next = is->buf;
\r
7581 InputThread(LPVOID arg)
\r
7586 is = (InputSource *) arg;
\r
7587 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7588 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7589 while (is->hThread != NULL) {
\r
7590 is->error = DoReadFile(is->hFile, is->next,
\r
7591 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7592 &is->count, &ovl);
\r
7593 if (is->error == NO_ERROR) {
\r
7594 is->next += is->count;
\r
7596 if (is->error == ERROR_BROKEN_PIPE) {
\r
7597 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7600 is->count = (DWORD) -1;
\r
7601 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7606 CheckForInputBufferFull( is );
\r
7608 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7610 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7612 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7615 CloseHandle(ovl.hEvent);
\r
7616 CloseHandle(is->hFile);
\r
7618 if (appData.debugMode) {
\r
7619 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7626 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7628 NonOvlInputThread(LPVOID arg)
\r
7635 is = (InputSource *) arg;
\r
7636 while (is->hThread != NULL) {
\r
7637 is->error = ReadFile(is->hFile, is->next,
\r
7638 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7639 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7640 if (is->error == NO_ERROR) {
\r
7641 /* Change CRLF to LF */
\r
7642 if (is->next > is->buf) {
\r
7644 i = is->count + 1;
\r
7652 if (prev == '\r' && *p == '\n') {
\r
7664 if (is->error == ERROR_BROKEN_PIPE) {
\r
7665 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7668 is->count = (DWORD) -1;
\r
7672 CheckForInputBufferFull( is );
\r
7674 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7676 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7678 if (is->count < 0) break; /* Quit on error */
\r
7680 CloseHandle(is->hFile);
\r
7685 SocketInputThread(LPVOID arg)
\r
7689 is = (InputSource *) arg;
\r
7690 while (is->hThread != NULL) {
\r
7691 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7692 if ((int)is->count == SOCKET_ERROR) {
\r
7693 is->count = (DWORD) -1;
\r
7694 is->error = WSAGetLastError();
\r
7696 is->error = NO_ERROR;
\r
7697 is->next += is->count;
\r
7698 if (is->count == 0 && is->second == is) {
\r
7699 /* End of file on stderr; quit with no message */
\r
7703 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7705 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7707 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7713 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7717 is = (InputSource *) lParam;
\r
7718 if (is->lineByLine) {
\r
7719 /* Feed in lines one by one */
\r
7720 char *p = is->buf;
\r
7722 while (q < is->next) {
\r
7723 if (*q++ == '\n') {
\r
7724 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7729 /* Move any partial line to the start of the buffer */
\r
7731 while (p < is->next) {
\r
7736 if (is->error != NO_ERROR || is->count == 0) {
\r
7737 /* Notify backend of the error. Note: If there was a partial
\r
7738 line at the end, it is not flushed through. */
\r
7739 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7742 /* Feed in the whole chunk of input at once */
\r
7743 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7744 is->next = is->buf;
\r
7748 /*---------------------------------------------------------------------------*\
\r
7750 * Menu enables. Used when setting various modes.
\r
7752 \*---------------------------------------------------------------------------*/
\r
7760 GreyRevert(Boolean grey)
\r
7761 { // [HGM] vari: for retracting variations in local mode
\r
7762 HMENU hmenu = GetMenu(hwndMain);
\r
7763 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7764 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7768 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7770 while (enab->item > 0) {
\r
7771 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7776 Enables gnuEnables[] = {
\r
7777 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7791 // Needed to switch from ncp to GNU mode on Engine Load
\r
7792 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7793 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7794 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7795 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7796 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7797 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7798 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7799 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7800 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7801 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7802 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7803 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7804 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7809 Enables icsEnables[] = {
\r
7810 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7818 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7825 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7833 Enables zippyEnables[] = {
\r
7834 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7835 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7836 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7837 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7842 Enables ncpEnables[] = {
\r
7843 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7853 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7868 Enables trainingOnEnables[] = {
\r
7869 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7881 Enables trainingOffEnables[] = {
\r
7882 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7884 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7885 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7886 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7887 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7888 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7889 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7890 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7894 /* These modify either ncpEnables or gnuEnables */
\r
7895 Enables cmailEnables[] = {
\r
7896 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7899 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7906 Enables machineThinkingEnables[] = {
\r
7907 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7920 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7926 Enables userThinkingEnables[] = {
\r
7927 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7936 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7937 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7940 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7941 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7942 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7946 /*---------------------------------------------------------------------------*\
\r
7948 * Front-end interface functions exported by XBoard.
\r
7949 * Functions appear in same order as prototypes in frontend.h.
\r
7951 \*---------------------------------------------------------------------------*/
\r
7953 CheckMark(UINT item, int state)
\r
7955 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7961 static UINT prevChecked = 0;
\r
7962 static int prevPausing = 0;
\r
7965 if (pausing != prevPausing) {
\r
7966 prevPausing = pausing;
\r
7967 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7968 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7969 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7972 switch (gameMode) {
\r
7973 case BeginningOfGame:
\r
7974 if (appData.icsActive)
\r
7975 nowChecked = IDM_IcsClient;
\r
7976 else if (appData.noChessProgram)
\r
7977 nowChecked = IDM_EditGame;
\r
7979 nowChecked = IDM_MachineBlack;
\r
7981 case MachinePlaysBlack:
\r
7982 nowChecked = IDM_MachineBlack;
\r
7984 case MachinePlaysWhite:
\r
7985 nowChecked = IDM_MachineWhite;
\r
7987 case TwoMachinesPlay:
\r
7988 nowChecked = IDM_TwoMachines;
\r
7991 nowChecked = IDM_AnalysisMode;
\r
7994 nowChecked = IDM_AnalyzeFile;
\r
7997 nowChecked = IDM_EditGame;
\r
7999 case PlayFromGameFile:
\r
8000 nowChecked = IDM_LoadGame;
\r
8002 case EditPosition:
\r
8003 nowChecked = IDM_EditPosition;
\r
8006 nowChecked = IDM_Training;
\r
8008 case IcsPlayingWhite:
\r
8009 case IcsPlayingBlack:
\r
8010 case IcsObserving:
\r
8012 nowChecked = IDM_IcsClient;
\r
8019 CheckMark(prevChecked, MF_UNCHECKED);
\r
8020 CheckMark(nowChecked, MF_CHECKED);
\r
8021 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8023 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8024 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8025 MF_BYCOMMAND|MF_ENABLED);
\r
8027 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8028 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8031 prevChecked = nowChecked;
\r
8033 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8034 if (appData.icsActive) {
\r
8035 if (appData.icsEngineAnalyze) {
\r
8036 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8038 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8041 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8047 HMENU hmenu = GetMenu(hwndMain);
\r
8048 SetMenuEnables(hmenu, icsEnables);
\r
8049 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8050 MF_BYCOMMAND|MF_ENABLED);
\r
8052 if (appData.zippyPlay) {
\r
8053 SetMenuEnables(hmenu, zippyEnables);
\r
8054 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8055 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8056 MF_BYCOMMAND|MF_ENABLED);
\r
8064 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8070 HMENU hmenu = GetMenu(hwndMain);
\r
8071 SetMenuEnables(hmenu, ncpEnables);
\r
8072 DrawMenuBar(hwndMain);
\r
8078 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8082 SetTrainingModeOn()
\r
8085 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8086 for (i = 0; i < N_BUTTONS; i++) {
\r
8087 if (buttonDesc[i].hwnd != NULL)
\r
8088 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8093 VOID SetTrainingModeOff()
\r
8096 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8097 for (i = 0; i < N_BUTTONS; i++) {
\r
8098 if (buttonDesc[i].hwnd != NULL)
\r
8099 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8105 SetUserThinkingEnables()
\r
8107 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8111 SetMachineThinkingEnables()
\r
8113 HMENU hMenu = GetMenu(hwndMain);
\r
8114 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8116 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8118 if (gameMode == MachinePlaysBlack) {
\r
8119 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8120 } else if (gameMode == MachinePlaysWhite) {
\r
8121 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8122 } else if (gameMode == TwoMachinesPlay) {
\r
8123 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8129 DisplayTitle(char *str)
\r
8131 char title[MSG_SIZ], *host;
\r
8132 if (str[0] != NULLCHAR) {
\r
8133 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8134 } else if (appData.icsActive) {
\r
8135 if (appData.icsCommPort[0] != NULLCHAR)
\r
8138 host = appData.icsHost;
\r
8139 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8140 } else if (appData.noChessProgram) {
\r
8141 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8143 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8144 strcat(title, ": ");
\r
8145 strcat(title, first.tidy);
\r
8147 SetWindowText(hwndMain, title);
\r
8152 DisplayMessage(char *str1, char *str2)
\r
8156 int remain = MESSAGE_TEXT_MAX - 1;
\r
8159 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8160 messageText[0] = NULLCHAR;
\r
8162 len = strlen(str1);
\r
8163 if (len > remain) len = remain;
\r
8164 strncpy(messageText, str1, len);
\r
8165 messageText[len] = NULLCHAR;
\r
8168 if (*str2 && remain >= 2) {
\r
8170 strcat(messageText, " ");
\r
8173 len = strlen(str2);
\r
8174 if (len > remain) len = remain;
\r
8175 strncat(messageText, str2, len);
\r
8177 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8178 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8180 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8184 hdc = GetDC(hwndMain);
\r
8185 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8186 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8187 &messageRect, messageText, strlen(messageText), NULL);
\r
8188 (void) SelectObject(hdc, oldFont);
\r
8189 (void) ReleaseDC(hwndMain, hdc);
\r
8193 DisplayError(char *str, int error)
\r
8195 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8199 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8201 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8202 NULL, error, LANG_NEUTRAL,
\r
8203 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8205 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8207 ErrorMap *em = errmap;
\r
8208 while (em->err != 0 && em->err != error) em++;
\r
8209 if (em->err != 0) {
\r
8210 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8212 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8217 ErrorPopUp(_("Error"), buf);
\r
8222 DisplayMoveError(char *str)
\r
8224 fromX = fromY = -1;
\r
8225 ClearHighlights();
\r
8226 DrawPosition(FALSE, NULL);
\r
8227 if (appData.popupMoveErrors) {
\r
8228 ErrorPopUp(_("Error"), str);
\r
8230 DisplayMessage(str, "");
\r
8231 moveErrorMessageUp = TRUE;
\r
8236 DisplayFatalError(char *str, int error, int exitStatus)
\r
8238 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8240 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8243 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8244 NULL, error, LANG_NEUTRAL,
\r
8245 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8247 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8249 ErrorMap *em = errmap;
\r
8250 while (em->err != 0 && em->err != error) em++;
\r
8251 if (em->err != 0) {
\r
8252 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8254 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8259 if (appData.debugMode) {
\r
8260 fprintf(debugFP, "%s: %s\n", label, str);
\r
8262 if (appData.popupExitMessage) {
\r
8263 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8264 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8266 ExitEvent(exitStatus);
\r
8271 DisplayInformation(char *str)
\r
8273 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8278 DisplayNote(char *str)
\r
8280 ErrorPopUp(_("Note"), str);
\r
8285 char *title, *question, *replyPrefix;
\r
8290 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8292 static QuestionParams *qp;
\r
8293 char reply[MSG_SIZ];
\r
8296 switch (message) {
\r
8297 case WM_INITDIALOG:
\r
8298 qp = (QuestionParams *) lParam;
\r
8299 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8300 Translate(hDlg, DLG_Question);
\r
8301 SetWindowText(hDlg, qp->title);
\r
8302 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8303 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8307 switch (LOWORD(wParam)) {
\r
8309 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8310 if (*reply) strcat(reply, " ");
\r
8311 len = strlen(reply);
\r
8312 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8313 strcat(reply, "\n");
\r
8314 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8315 EndDialog(hDlg, TRUE);
\r
8316 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8319 EndDialog(hDlg, FALSE);
\r
8330 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8332 QuestionParams qp;
\r
8336 qp.question = question;
\r
8337 qp.replyPrefix = replyPrefix;
\r
8339 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8340 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8341 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8342 FreeProcInstance(lpProc);
\r
8345 /* [AS] Pick FRC position */
\r
8346 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8348 static int * lpIndexFRC;
\r
8354 case WM_INITDIALOG:
\r
8355 lpIndexFRC = (int *) lParam;
\r
8357 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8358 Translate(hDlg, DLG_NewGameFRC);
\r
8360 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8361 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8362 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8363 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8368 switch( LOWORD(wParam) ) {
\r
8370 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8371 EndDialog( hDlg, 0 );
\r
8372 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8375 EndDialog( hDlg, 1 );
\r
8377 case IDC_NFG_Edit:
\r
8378 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8379 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8381 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8384 case IDC_NFG_Random:
\r
8385 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8386 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8399 int index = appData.defaultFrcPosition;
\r
8400 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8402 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8404 if( result == 0 ) {
\r
8405 appData.defaultFrcPosition = index;
\r
8411 /* [AS] Game list options. Refactored by HGM */
\r
8413 HWND gameListOptionsDialog;
\r
8415 // low-level front-end: clear text edit / list widget
\r
8419 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8422 // low-level front-end: clear text edit / list widget
\r
8424 GLT_DeSelectList()
\r
8426 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8429 // low-level front-end: append line to text edit / list widget
\r
8431 GLT_AddToList( char *name )
\r
8434 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8438 // low-level front-end: get line from text edit / list widget
\r
8440 GLT_GetFromList( int index, char *name )
\r
8443 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8449 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8451 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8452 int idx2 = idx1 + delta;
\r
8453 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8455 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8458 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8459 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8460 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8461 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8465 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8469 case WM_INITDIALOG:
\r
8470 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8472 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8473 Translate(hDlg, DLG_GameListOptions);
\r
8475 /* Initialize list */
\r
8476 GLT_TagsToList( lpUserGLT );
\r
8478 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8483 switch( LOWORD(wParam) ) {
\r
8486 EndDialog( hDlg, 0 );
\r
8489 EndDialog( hDlg, 1 );
\r
8492 case IDC_GLT_Default:
\r
8493 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8496 case IDC_GLT_Restore:
\r
8497 GLT_TagsToList( appData.gameListTags );
\r
8501 GLT_MoveSelection( hDlg, -1 );
\r
8504 case IDC_GLT_Down:
\r
8505 GLT_MoveSelection( hDlg, +1 );
\r
8515 int GameListOptions()
\r
8518 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8520 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8522 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8524 if( result == 0 ) {
\r
8525 /* [AS] Memory leak here! */
\r
8526 appData.gameListTags = strdup( lpUserGLT );
\r
8533 DisplayIcsInteractionTitle(char *str)
\r
8535 char consoleTitle[MSG_SIZ];
\r
8537 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8538 SetWindowText(hwndConsole, consoleTitle);
\r
8542 DrawPosition(int fullRedraw, Board board)
\r
8544 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8547 void NotifyFrontendLogin()
\r
8550 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8556 fromX = fromY = -1;
\r
8557 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8558 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8559 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8560 dragInfo.lastpos = dragInfo.pos;
\r
8561 dragInfo.start.x = dragInfo.start.y = -1;
\r
8562 dragInfo.from = dragInfo.start;
\r
8564 DrawPosition(TRUE, NULL);
\r
8571 CommentPopUp(char *title, char *str)
\r
8573 HWND hwnd = GetActiveWindow();
\r
8574 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8576 SetActiveWindow(hwnd);
\r
8580 CommentPopDown(void)
\r
8582 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8583 if (commentDialog) {
\r
8584 ShowWindow(commentDialog, SW_HIDE);
\r
8586 commentUp = FALSE;
\r
8590 EditCommentPopUp(int index, char *title, char *str)
\r
8592 EitherCommentPopUp(index, title, str, TRUE);
\r
8599 MyPlaySound(&sounds[(int)SoundMove]);
\r
8602 VOID PlayIcsWinSound()
\r
8604 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8607 VOID PlayIcsLossSound()
\r
8609 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8612 VOID PlayIcsDrawSound()
\r
8614 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8617 VOID PlayIcsUnfinishedSound()
\r
8619 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8625 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8631 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8639 consoleEcho = TRUE;
\r
8640 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8641 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8642 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8651 consoleEcho = FALSE;
\r
8652 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8653 /* This works OK: set text and background both to the same color */
\r
8655 cf.crTextColor = COLOR_ECHOOFF;
\r
8656 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8657 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8660 /* No Raw()...? */
\r
8662 void Colorize(ColorClass cc, int continuation)
\r
8664 currentColorClass = cc;
\r
8665 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8666 consoleCF.crTextColor = textAttribs[cc].color;
\r
8667 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8668 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8674 static char buf[MSG_SIZ];
\r
8675 DWORD bufsiz = MSG_SIZ;
\r
8677 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8678 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8680 if (!GetUserName(buf, &bufsiz)) {
\r
8681 /*DisplayError("Error getting user name", GetLastError());*/
\r
8682 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8690 static char buf[MSG_SIZ];
\r
8691 DWORD bufsiz = MSG_SIZ;
\r
8693 if (!GetComputerName(buf, &bufsiz)) {
\r
8694 /*DisplayError("Error getting host name", GetLastError());*/
\r
8695 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8702 ClockTimerRunning()
\r
8704 return clockTimerEvent != 0;
\r
8710 if (clockTimerEvent == 0) return FALSE;
\r
8711 KillTimer(hwndMain, clockTimerEvent);
\r
8712 clockTimerEvent = 0;
\r
8717 StartClockTimer(long millisec)
\r
8719 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8720 (UINT) millisec, NULL);
\r
8724 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8727 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8729 if(appData.noGUI) return;
\r
8730 hdc = GetDC(hwndMain);
\r
8731 if (!IsIconic(hwndMain)) {
\r
8732 DisplayAClock(hdc, timeRemaining, highlight,
\r
8733 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8735 if (highlight && iconCurrent == iconBlack) {
\r
8736 iconCurrent = iconWhite;
\r
8737 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8738 if (IsIconic(hwndMain)) {
\r
8739 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8742 (void) ReleaseDC(hwndMain, hdc);
\r
8744 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8748 DisplayBlackClock(long timeRemaining, int highlight)
\r
8751 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8753 if(appData.noGUI) return;
\r
8754 hdc = GetDC(hwndMain);
\r
8755 if (!IsIconic(hwndMain)) {
\r
8756 DisplayAClock(hdc, timeRemaining, highlight,
\r
8757 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8759 if (highlight && iconCurrent == iconWhite) {
\r
8760 iconCurrent = iconBlack;
\r
8761 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8762 if (IsIconic(hwndMain)) {
\r
8763 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8766 (void) ReleaseDC(hwndMain, hdc);
\r
8768 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8773 LoadGameTimerRunning()
\r
8775 return loadGameTimerEvent != 0;
\r
8779 StopLoadGameTimer()
\r
8781 if (loadGameTimerEvent == 0) return FALSE;
\r
8782 KillTimer(hwndMain, loadGameTimerEvent);
\r
8783 loadGameTimerEvent = 0;
\r
8788 StartLoadGameTimer(long millisec)
\r
8790 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8791 (UINT) millisec, NULL);
\r
8799 char fileTitle[MSG_SIZ];
\r
8801 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8802 f = OpenFileDialog(hwndMain, "a", defName,
\r
8803 appData.oldSaveStyle ? "gam" : "pgn",
\r
8805 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8807 SaveGame(f, 0, "");
\r
8814 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8816 if (delayedTimerEvent != 0) {
\r
8817 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8818 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8820 KillTimer(hwndMain, delayedTimerEvent);
\r
8821 delayedTimerEvent = 0;
\r
8822 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8823 delayedTimerCallback();
\r
8825 delayedTimerCallback = cb;
\r
8826 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8827 (UINT) millisec, NULL);
\r
8830 DelayedEventCallback
\r
8833 if (delayedTimerEvent) {
\r
8834 return delayedTimerCallback;
\r
8841 CancelDelayedEvent()
\r
8843 if (delayedTimerEvent) {
\r
8844 KillTimer(hwndMain, delayedTimerEvent);
\r
8845 delayedTimerEvent = 0;
\r
8849 DWORD GetWin32Priority(int nice)
\r
8850 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8852 REALTIME_PRIORITY_CLASS 0x00000100
\r
8853 HIGH_PRIORITY_CLASS 0x00000080
\r
8854 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8855 NORMAL_PRIORITY_CLASS 0x00000020
\r
8856 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8857 IDLE_PRIORITY_CLASS 0x00000040
\r
8859 if (nice < -15) return 0x00000080;
\r
8860 if (nice < 0) return 0x00008000;
\r
8861 if (nice == 0) return 0x00000020;
\r
8862 if (nice < 15) return 0x00004000;
\r
8863 return 0x00000040;
\r
8866 void RunCommand(char *cmdLine)
\r
8868 /* Now create the child process. */
\r
8869 STARTUPINFO siStartInfo;
\r
8870 PROCESS_INFORMATION piProcInfo;
\r
8872 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8873 siStartInfo.lpReserved = NULL;
\r
8874 siStartInfo.lpDesktop = NULL;
\r
8875 siStartInfo.lpTitle = NULL;
\r
8876 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8877 siStartInfo.cbReserved2 = 0;
\r
8878 siStartInfo.lpReserved2 = NULL;
\r
8879 siStartInfo.hStdInput = NULL;
\r
8880 siStartInfo.hStdOutput = NULL;
\r
8881 siStartInfo.hStdError = NULL;
\r
8883 CreateProcess(NULL,
\r
8884 cmdLine, /* command line */
\r
8885 NULL, /* process security attributes */
\r
8886 NULL, /* primary thread security attrs */
\r
8887 TRUE, /* handles are inherited */
\r
8888 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8889 NULL, /* use parent's environment */
\r
8891 &siStartInfo, /* STARTUPINFO pointer */
\r
8892 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8894 CloseHandle(piProcInfo.hThread);
\r
8897 /* Start a child process running the given program.
\r
8898 The process's standard output can be read from "from", and its
\r
8899 standard input can be written to "to".
\r
8900 Exit with fatal error if anything goes wrong.
\r
8901 Returns an opaque pointer that can be used to destroy the process
\r
8905 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8907 #define BUFSIZE 4096
\r
8909 HANDLE hChildStdinRd, hChildStdinWr,
\r
8910 hChildStdoutRd, hChildStdoutWr;
\r
8911 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8912 SECURITY_ATTRIBUTES saAttr;
\r
8914 PROCESS_INFORMATION piProcInfo;
\r
8915 STARTUPINFO siStartInfo;
\r
8917 char buf[MSG_SIZ];
\r
8920 if (appData.debugMode) {
\r
8921 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8926 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8927 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8928 saAttr.bInheritHandle = TRUE;
\r
8929 saAttr.lpSecurityDescriptor = NULL;
\r
8932 * The steps for redirecting child's STDOUT:
\r
8933 * 1. Create anonymous pipe to be STDOUT for child.
\r
8934 * 2. Create a noninheritable duplicate of read handle,
\r
8935 * and close the inheritable read handle.
\r
8938 /* Create a pipe for the child's STDOUT. */
\r
8939 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8940 return GetLastError();
\r
8943 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8944 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8945 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8946 FALSE, /* not inherited */
\r
8947 DUPLICATE_SAME_ACCESS);
\r
8949 return GetLastError();
\r
8951 CloseHandle(hChildStdoutRd);
\r
8954 * The steps for redirecting child's STDIN:
\r
8955 * 1. Create anonymous pipe to be STDIN for child.
\r
8956 * 2. Create a noninheritable duplicate of write handle,
\r
8957 * and close the inheritable write handle.
\r
8960 /* Create a pipe for the child's STDIN. */
\r
8961 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8962 return GetLastError();
\r
8965 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8966 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8967 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8968 FALSE, /* not inherited */
\r
8969 DUPLICATE_SAME_ACCESS);
\r
8971 return GetLastError();
\r
8973 CloseHandle(hChildStdinWr);
\r
8975 /* Arrange to (1) look in dir for the child .exe file, and
\r
8976 * (2) have dir be the child's working directory. Interpret
\r
8977 * dir relative to the directory WinBoard loaded from. */
\r
8978 GetCurrentDirectory(MSG_SIZ, buf);
\r
8979 SetCurrentDirectory(installDir);
\r
8980 SetCurrentDirectory(dir);
\r
8982 /* Now create the child process. */
\r
8984 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8985 siStartInfo.lpReserved = NULL;
\r
8986 siStartInfo.lpDesktop = NULL;
\r
8987 siStartInfo.lpTitle = NULL;
\r
8988 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8989 siStartInfo.cbReserved2 = 0;
\r
8990 siStartInfo.lpReserved2 = NULL;
\r
8991 siStartInfo.hStdInput = hChildStdinRd;
\r
8992 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8993 siStartInfo.hStdError = hChildStdoutWr;
\r
8995 fSuccess = CreateProcess(NULL,
\r
8996 cmdLine, /* command line */
\r
8997 NULL, /* process security attributes */
\r
8998 NULL, /* primary thread security attrs */
\r
8999 TRUE, /* handles are inherited */
\r
9000 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9001 NULL, /* use parent's environment */
\r
9003 &siStartInfo, /* STARTUPINFO pointer */
\r
9004 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9006 err = GetLastError();
\r
9007 SetCurrentDirectory(buf); /* return to prev directory */
\r
9012 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9013 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9014 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9017 /* Close the handles we don't need in the parent */
\r
9018 CloseHandle(piProcInfo.hThread);
\r
9019 CloseHandle(hChildStdinRd);
\r
9020 CloseHandle(hChildStdoutWr);
\r
9022 /* Prepare return value */
\r
9023 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9024 cp->kind = CPReal;
\r
9025 cp->hProcess = piProcInfo.hProcess;
\r
9026 cp->pid = piProcInfo.dwProcessId;
\r
9027 cp->hFrom = hChildStdoutRdDup;
\r
9028 cp->hTo = hChildStdinWrDup;
\r
9030 *pr = (void *) cp;
\r
9032 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9033 2000 where engines sometimes don't see the initial command(s)
\r
9034 from WinBoard and hang. I don't understand how that can happen,
\r
9035 but the Sleep is harmless, so I've put it in. Others have also
\r
9036 reported what may be the same problem, so hopefully this will fix
\r
9037 it for them too. */
\r
9045 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9047 ChildProc *cp; int result;
\r
9049 cp = (ChildProc *) pr;
\r
9050 if (cp == NULL) return;
\r
9052 switch (cp->kind) {
\r
9054 /* TerminateProcess is considered harmful, so... */
\r
9055 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9056 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9057 /* The following doesn't work because the chess program
\r
9058 doesn't "have the same console" as WinBoard. Maybe
\r
9059 we could arrange for this even though neither WinBoard
\r
9060 nor the chess program uses a console for stdio? */
\r
9061 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9063 /* [AS] Special termination modes for misbehaving programs... */
\r
9064 if( signal == 9 ) {
\r
9065 result = TerminateProcess( cp->hProcess, 0 );
\r
9067 if ( appData.debugMode) {
\r
9068 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9071 else if( signal == 10 ) {
\r
9072 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9074 if( dw != WAIT_OBJECT_0 ) {
\r
9075 result = TerminateProcess( cp->hProcess, 0 );
\r
9077 if ( appData.debugMode) {
\r
9078 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9084 CloseHandle(cp->hProcess);
\r
9088 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9092 closesocket(cp->sock);
\r
9097 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9098 closesocket(cp->sock);
\r
9099 closesocket(cp->sock2);
\r
9107 InterruptChildProcess(ProcRef pr)
\r
9111 cp = (ChildProc *) pr;
\r
9112 if (cp == NULL) return;
\r
9113 switch (cp->kind) {
\r
9115 /* The following doesn't work because the chess program
\r
9116 doesn't "have the same console" as WinBoard. Maybe
\r
9117 we could arrange for this even though neither WinBoard
\r
9118 nor the chess program uses a console for stdio */
\r
9119 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9124 /* Can't interrupt */
\r
9128 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9135 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9137 char cmdLine[MSG_SIZ];
\r
9139 if (port[0] == NULLCHAR) {
\r
9140 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9142 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9144 return StartChildProcess(cmdLine, "", pr);
\r
9148 /* Code to open TCP sockets */
\r
9151 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9156 struct sockaddr_in sa, mysa;
\r
9157 struct hostent FAR *hp;
\r
9158 unsigned short uport;
\r
9159 WORD wVersionRequested;
\r
9162 /* Initialize socket DLL */
\r
9163 wVersionRequested = MAKEWORD(1, 1);
\r
9164 err = WSAStartup(wVersionRequested, &wsaData);
\r
9165 if (err != 0) return err;
\r
9168 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9169 err = WSAGetLastError();
\r
9174 /* Bind local address using (mostly) don't-care values.
\r
9176 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9177 mysa.sin_family = AF_INET;
\r
9178 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9179 uport = (unsigned short) 0;
\r
9180 mysa.sin_port = htons(uport);
\r
9181 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9182 == SOCKET_ERROR) {
\r
9183 err = WSAGetLastError();
\r
9188 /* Resolve remote host name */
\r
9189 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9190 if (!(hp = gethostbyname(host))) {
\r
9191 unsigned int b0, b1, b2, b3;
\r
9193 err = WSAGetLastError();
\r
9195 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9196 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9197 hp->h_addrtype = AF_INET;
\r
9199 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9200 hp->h_addr_list[0] = (char *) malloc(4);
\r
9201 hp->h_addr_list[0][0] = (char) b0;
\r
9202 hp->h_addr_list[0][1] = (char) b1;
\r
9203 hp->h_addr_list[0][2] = (char) b2;
\r
9204 hp->h_addr_list[0][3] = (char) b3;
\r
9210 sa.sin_family = hp->h_addrtype;
\r
9211 uport = (unsigned short) atoi(port);
\r
9212 sa.sin_port = htons(uport);
\r
9213 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9215 /* Make connection */
\r
9216 if (connect(s, (struct sockaddr *) &sa,
\r
9217 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9218 err = WSAGetLastError();
\r
9223 /* Prepare return value */
\r
9224 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9225 cp->kind = CPSock;
\r
9227 *pr = (ProcRef *) cp;
\r
9233 OpenCommPort(char *name, ProcRef *pr)
\r
9238 char fullname[MSG_SIZ];
\r
9240 if (*name != '\\')
\r
9241 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9243 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9245 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9246 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9247 if (h == (HANDLE) -1) {
\r
9248 return GetLastError();
\r
9252 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9254 /* Accumulate characters until a 100ms pause, then parse */
\r
9255 ct.ReadIntervalTimeout = 100;
\r
9256 ct.ReadTotalTimeoutMultiplier = 0;
\r
9257 ct.ReadTotalTimeoutConstant = 0;
\r
9258 ct.WriteTotalTimeoutMultiplier = 0;
\r
9259 ct.WriteTotalTimeoutConstant = 0;
\r
9260 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9262 /* Prepare return value */
\r
9263 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9264 cp->kind = CPComm;
\r
9267 *pr = (ProcRef *) cp;
\r
9273 OpenLoopback(ProcRef *pr)
\r
9275 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9281 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9286 struct sockaddr_in sa, mysa;
\r
9287 struct hostent FAR *hp;
\r
9288 unsigned short uport;
\r
9289 WORD wVersionRequested;
\r
9292 char stderrPortStr[MSG_SIZ];
\r
9294 /* Initialize socket DLL */
\r
9295 wVersionRequested = MAKEWORD(1, 1);
\r
9296 err = WSAStartup(wVersionRequested, &wsaData);
\r
9297 if (err != 0) return err;
\r
9299 /* Resolve remote host name */
\r
9300 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9301 if (!(hp = gethostbyname(host))) {
\r
9302 unsigned int b0, b1, b2, b3;
\r
9304 err = WSAGetLastError();
\r
9306 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9307 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9308 hp->h_addrtype = AF_INET;
\r
9310 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9311 hp->h_addr_list[0] = (char *) malloc(4);
\r
9312 hp->h_addr_list[0][0] = (char) b0;
\r
9313 hp->h_addr_list[0][1] = (char) b1;
\r
9314 hp->h_addr_list[0][2] = (char) b2;
\r
9315 hp->h_addr_list[0][3] = (char) b3;
\r
9321 sa.sin_family = hp->h_addrtype;
\r
9322 uport = (unsigned short) 514;
\r
9323 sa.sin_port = htons(uport);
\r
9324 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9326 /* Bind local socket to unused "privileged" port address
\r
9328 s = INVALID_SOCKET;
\r
9329 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9330 mysa.sin_family = AF_INET;
\r
9331 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9332 for (fromPort = 1023;; fromPort--) {
\r
9333 if (fromPort < 0) {
\r
9335 return WSAEADDRINUSE;
\r
9337 if (s == INVALID_SOCKET) {
\r
9338 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9339 err = WSAGetLastError();
\r
9344 uport = (unsigned short) fromPort;
\r
9345 mysa.sin_port = htons(uport);
\r
9346 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9347 == SOCKET_ERROR) {
\r
9348 err = WSAGetLastError();
\r
9349 if (err == WSAEADDRINUSE) continue;
\r
9353 if (connect(s, (struct sockaddr *) &sa,
\r
9354 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9355 err = WSAGetLastError();
\r
9356 if (err == WSAEADDRINUSE) {
\r
9367 /* Bind stderr local socket to unused "privileged" port address
\r
9369 s2 = INVALID_SOCKET;
\r
9370 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9371 mysa.sin_family = AF_INET;
\r
9372 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9373 for (fromPort = 1023;; fromPort--) {
\r
9374 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9375 if (fromPort < 0) {
\r
9376 (void) closesocket(s);
\r
9378 return WSAEADDRINUSE;
\r
9380 if (s2 == INVALID_SOCKET) {
\r
9381 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9382 err = WSAGetLastError();
\r
9388 uport = (unsigned short) fromPort;
\r
9389 mysa.sin_port = htons(uport);
\r
9390 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9391 == SOCKET_ERROR) {
\r
9392 err = WSAGetLastError();
\r
9393 if (err == WSAEADDRINUSE) continue;
\r
9394 (void) closesocket(s);
\r
9398 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9399 err = WSAGetLastError();
\r
9400 if (err == WSAEADDRINUSE) {
\r
9402 s2 = INVALID_SOCKET;
\r
9405 (void) closesocket(s);
\r
9406 (void) closesocket(s2);
\r
9412 prevStderrPort = fromPort; // remember port used
\r
9413 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9415 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9416 err = WSAGetLastError();
\r
9417 (void) closesocket(s);
\r
9418 (void) closesocket(s2);
\r
9423 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9424 err = WSAGetLastError();
\r
9425 (void) closesocket(s);
\r
9426 (void) closesocket(s2);
\r
9430 if (*user == NULLCHAR) user = UserName();
\r
9431 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9432 err = WSAGetLastError();
\r
9433 (void) closesocket(s);
\r
9434 (void) closesocket(s2);
\r
9438 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9439 err = WSAGetLastError();
\r
9440 (void) closesocket(s);
\r
9441 (void) closesocket(s2);
\r
9446 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9447 err = WSAGetLastError();
\r
9448 (void) closesocket(s);
\r
9449 (void) closesocket(s2);
\r
9453 (void) closesocket(s2); /* Stop listening */
\r
9455 /* Prepare return value */
\r
9456 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9457 cp->kind = CPRcmd;
\r
9460 *pr = (ProcRef *) cp;
\r
9467 AddInputSource(ProcRef pr, int lineByLine,
\r
9468 InputCallback func, VOIDSTAR closure)
\r
9470 InputSource *is, *is2 = NULL;
\r
9471 ChildProc *cp = (ChildProc *) pr;
\r
9473 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9474 is->lineByLine = lineByLine;
\r
9476 is->closure = closure;
\r
9477 is->second = NULL;
\r
9478 is->next = is->buf;
\r
9479 if (pr == NoProc) {
\r
9480 is->kind = CPReal;
\r
9481 consoleInputSource = is;
\r
9483 is->kind = cp->kind;
\r
9485 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9486 we create all threads suspended so that the is->hThread variable can be
\r
9487 safely assigned, then let the threads start with ResumeThread.
\r
9489 switch (cp->kind) {
\r
9491 is->hFile = cp->hFrom;
\r
9492 cp->hFrom = NULL; /* now owned by InputThread */
\r
9494 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9495 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9499 is->hFile = cp->hFrom;
\r
9500 cp->hFrom = NULL; /* now owned by InputThread */
\r
9502 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9503 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9507 is->sock = cp->sock;
\r
9509 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9510 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9514 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9516 is->sock = cp->sock;
\r
9518 is2->sock = cp->sock2;
\r
9519 is2->second = is2;
\r
9521 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9522 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9524 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9525 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9529 if( is->hThread != NULL ) {
\r
9530 ResumeThread( is->hThread );
\r
9533 if( is2 != NULL && is2->hThread != NULL ) {
\r
9534 ResumeThread( is2->hThread );
\r
9538 return (InputSourceRef) is;
\r
9542 RemoveInputSource(InputSourceRef isr)
\r
9546 is = (InputSource *) isr;
\r
9547 is->hThread = NULL; /* tell thread to stop */
\r
9548 CloseHandle(is->hThread);
\r
9549 if (is->second != NULL) {
\r
9550 is->second->hThread = NULL;
\r
9551 CloseHandle(is->second->hThread);
\r
9555 int no_wrap(char *message, int count)
\r
9557 ConsoleOutput(message, count, FALSE);
\r
9562 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9565 int outCount = SOCKET_ERROR;
\r
9566 ChildProc *cp = (ChildProc *) pr;
\r
9567 static OVERLAPPED ovl;
\r
9568 static int line = 0;
\r
9572 if (appData.noJoin || !appData.useInternalWrap)
\r
9573 return no_wrap(message, count);
\r
9576 int width = get_term_width();
\r
9577 int len = wrap(NULL, message, count, width, &line);
\r
9578 char *msg = malloc(len);
\r
9582 return no_wrap(message, count);
\r
9585 dbgchk = wrap(msg, message, count, width, &line);
\r
9586 if (dbgchk != len && appData.debugMode)
\r
9587 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9588 ConsoleOutput(msg, len, FALSE);
\r
9595 if (ovl.hEvent == NULL) {
\r
9596 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9598 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9600 switch (cp->kind) {
\r
9603 outCount = send(cp->sock, message, count, 0);
\r
9604 if (outCount == SOCKET_ERROR) {
\r
9605 *outError = WSAGetLastError();
\r
9607 *outError = NO_ERROR;
\r
9612 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9613 &dOutCount, NULL)) {
\r
9614 *outError = NO_ERROR;
\r
9615 outCount = (int) dOutCount;
\r
9617 *outError = GetLastError();
\r
9622 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9623 &dOutCount, &ovl);
\r
9624 if (*outError == NO_ERROR) {
\r
9625 outCount = (int) dOutCount;
\r
9635 if(n != 0) Sleep(n);
\r
9639 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9642 /* Ignore delay, not implemented for WinBoard */
\r
9643 return OutputToProcess(pr, message, count, outError);
\r
9648 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9649 char *buf, int count, int error)
\r
9651 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9654 /* see wgamelist.c for Game List functions */
\r
9655 /* see wedittags.c for Edit Tags functions */
\r
9662 char buf[MSG_SIZ];
\r
9665 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9666 f = fopen(buf, "r");
\r
9668 ProcessICSInitScript(f);
\r
9676 StartAnalysisClock()
\r
9678 if (analysisTimerEvent) return;
\r
9679 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9680 (UINT) 2000, NULL);
\r
9684 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9686 highlightInfo.sq[0].x = fromX;
\r
9687 highlightInfo.sq[0].y = fromY;
\r
9688 highlightInfo.sq[1].x = toX;
\r
9689 highlightInfo.sq[1].y = toY;
\r
9695 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9696 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9700 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9702 premoveHighlightInfo.sq[0].x = fromX;
\r
9703 premoveHighlightInfo.sq[0].y = fromY;
\r
9704 premoveHighlightInfo.sq[1].x = toX;
\r
9705 premoveHighlightInfo.sq[1].y = toY;
\r
9709 ClearPremoveHighlights()
\r
9711 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9712 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9716 ShutDownFrontEnd()
\r
9718 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9719 DeleteClipboardTempFiles();
\r
9725 if (IsIconic(hwndMain))
\r
9726 ShowWindow(hwndMain, SW_RESTORE);
\r
9728 SetActiveWindow(hwndMain);
\r
9732 * Prototypes for animation support routines
\r
9734 static void ScreenSquare(int column, int row, POINT * pt);
\r
9735 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9736 POINT frames[], int * nFrames);
\r
9742 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9743 { // [HGM] atomic: animate blast wave
\r
9746 explodeInfo.fromX = fromX;
\r
9747 explodeInfo.fromY = fromY;
\r
9748 explodeInfo.toX = toX;
\r
9749 explodeInfo.toY = toY;
\r
9750 for(i=1; i<4*kFactor; i++) {
\r
9751 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9752 DrawPosition(FALSE, board);
\r
9753 Sleep(appData.animSpeed);
\r
9755 explodeInfo.radius = 0;
\r
9756 DrawPosition(TRUE, board);
\r
9760 AnimateMove(board, fromX, fromY, toX, toY)
\r
9767 ChessSquare piece;
\r
9768 POINT start, finish, mid;
\r
9769 POINT frames[kFactor * 2 + 1];
\r
9772 if (!appData.animate) return;
\r
9773 if (doingSizing) return;
\r
9774 if (fromY < 0 || fromX < 0) return;
\r
9775 piece = board[fromY][fromX];
\r
9776 if (piece >= EmptySquare) return;
\r
9778 ScreenSquare(fromX, fromY, &start);
\r
9779 ScreenSquare(toX, toY, &finish);
\r
9781 /* All moves except knight jumps move in straight line */
\r
9782 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9783 mid.x = start.x + (finish.x - start.x) / 2;
\r
9784 mid.y = start.y + (finish.y - start.y) / 2;
\r
9786 /* Knight: make straight movement then diagonal */
\r
9787 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9788 mid.x = start.x + (finish.x - start.x) / 2;
\r
9792 mid.y = start.y + (finish.y - start.y) / 2;
\r
9796 /* Don't use as many frames for very short moves */
\r
9797 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9798 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9800 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9802 animInfo.from.x = fromX;
\r
9803 animInfo.from.y = fromY;
\r
9804 animInfo.to.x = toX;
\r
9805 animInfo.to.y = toY;
\r
9806 animInfo.lastpos = start;
\r
9807 animInfo.piece = piece;
\r
9808 for (n = 0; n < nFrames; n++) {
\r
9809 animInfo.pos = frames[n];
\r
9810 DrawPosition(FALSE, NULL);
\r
9811 animInfo.lastpos = animInfo.pos;
\r
9812 Sleep(appData.animSpeed);
\r
9814 animInfo.pos = finish;
\r
9815 DrawPosition(FALSE, NULL);
\r
9816 animInfo.piece = EmptySquare;
\r
9817 Explode(board, fromX, fromY, toX, toY);
\r
9820 /* Convert board position to corner of screen rect and color */
\r
9823 ScreenSquare(column, row, pt)
\r
9824 int column; int row; POINT * pt;
\r
9827 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9828 pt->y = lineGap + row * (squareSize + lineGap);
\r
9830 pt->x = lineGap + column * (squareSize + lineGap);
\r
9831 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9835 /* Generate a series of frame coords from start->mid->finish.
\r
9836 The movement rate doubles until the half way point is
\r
9837 reached, then halves back down to the final destination,
\r
9838 which gives a nice slow in/out effect. The algorithmn
\r
9839 may seem to generate too many intermediates for short
\r
9840 moves, but remember that the purpose is to attract the
\r
9841 viewers attention to the piece about to be moved and
\r
9842 then to where it ends up. Too few frames would be less
\r
9846 Tween(start, mid, finish, factor, frames, nFrames)
\r
9847 POINT * start; POINT * mid;
\r
9848 POINT * finish; int factor;
\r
9849 POINT frames[]; int * nFrames;
\r
9851 int n, fraction = 1, count = 0;
\r
9853 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9854 for (n = 0; n < factor; n++)
\r
9856 for (n = 0; n < factor; n++) {
\r
9857 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9858 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9860 fraction = fraction / 2;
\r
9864 frames[count] = *mid;
\r
9867 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9869 for (n = 0; n < factor; n++) {
\r
9870 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9871 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9873 fraction = fraction * 2;
\r
9879 SettingsPopUp(ChessProgramState *cps)
\r
9880 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9881 EngineOptionsPopup(savedHwnd, cps);
\r
9884 int flock(int fid, int code)
\r
9886 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9890 ov.OffsetHigh = 0;
\r
9892 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9893 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9894 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9895 default: return -1;
\r