2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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
96 #define DATADIR "~~"
\r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 int myrandom(void);
\r
101 void mysrandom(unsigned int seed);
\r
103 extern int whiteFlag, blackFlag;
\r
104 Boolean flipClock = FALSE;
\r
105 extern HANDLE chatHandle[];
\r
106 extern enum ICS_TYPE ics_type;
\r
108 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
109 int MyGetFullPathName P((char *name, char *fullname));
\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
111 VOID NewVariantPopup(HWND hwnd);
\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
113 /*char*/int promoChar));
\r
114 void DisplayMove P((int moveNumber));
\r
115 void ChatPopUp P((char *s));
\r
117 ChessSquare piece;
\r
118 POINT pos; /* window coordinates of current pos */
\r
119 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
120 POINT from; /* board coordinates of the piece's orig pos */
\r
121 POINT to; /* board coordinates of the piece's new pos */
\r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
127 POINT start; /* window coordinates of start pos */
\r
128 POINT pos; /* window coordinates of current pos */
\r
129 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
130 POINT from; /* board coordinates of the piece's orig pos */
\r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
137 POINT sq[2]; /* board coordinates of from, to squares */
\r
140 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
145 typedef struct { // [HGM] atomic
\r
146 int fromX, fromY, toX, toY, radius;
\r
149 static ExplodeInfo explodeInfo;
\r
151 /* Window class names */
\r
152 char szAppName[] = "WinBoard";
\r
153 char szConsoleName[] = "WBConsole";
\r
155 /* Title bar text */
\r
156 char szTitle[] = "WinBoard";
\r
157 char szConsoleTitle[] = "I C S Interaction";
\r
160 char *settingsFileName;
\r
161 Boolean saveSettingsOnExit;
\r
162 char installDir[MSG_SIZ];
\r
163 int errorExitStatus;
\r
165 BoardSize boardSize;
\r
166 Boolean chessProgram;
\r
167 //static int boardX, boardY;
\r
168 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
169 int squareSize, lineGap, minorSize, border;
\r
170 static int winW, winH;
\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
172 static int logoHeight = 0;
\r
173 static char messageText[MESSAGE_TEXT_MAX];
\r
174 static int clockTimerEvent = 0;
\r
175 static int loadGameTimerEvent = 0;
\r
176 static int analysisTimerEvent = 0;
\r
177 static DelayedEventCallback delayedTimerCallback;
\r
178 static int delayedTimerEvent = 0;
\r
179 static int buttonCount = 2;
\r
180 char *icsTextMenuString;
\r
182 char *firstChessProgramNames;
\r
183 char *secondChessProgramNames;
\r
185 #define PALETTESIZE 256
\r
187 HINSTANCE hInst; /* current instance */
\r
188 Boolean alwaysOnTop = FALSE;
\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
191 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
194 ColorClass currentColorClass;
\r
196 static HWND savedHwnd;
\r
197 HWND hCommPort = NULL; /* currently open comm port */
\r
198 static HWND hwndPause; /* pause button */
\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
201 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
202 explodeBrush, /* [HGM] atomic */
\r
203 markerBrush[8], /* [HGM] markers */
\r
204 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
207 static HPEN gridPen = NULL;
\r
208 static HPEN highlightPen = NULL;
\r
209 static HPEN premovePen = NULL;
\r
210 static NPLOGPALETTE pLogPal;
\r
211 static BOOL paletteChanged = FALSE;
\r
212 static HICON iconWhite, iconBlack, iconCurrent;
\r
213 static int doingSizing = FALSE;
\r
214 static int lastSizing = 0;
\r
215 static int prevStderrPort;
\r
216 static HBITMAP userLogo;
\r
218 static HBITMAP liteBackTexture = NULL;
\r
219 static HBITMAP darkBackTexture = NULL;
\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
222 static int backTextureSquareSize = 0;
\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
225 #if __GNUC__ && !defined(_winmajor)
\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
229 #if defined(_winmajor)
\r
230 #define oldDialog (_winmajor < 4)
\r
232 #define oldDialog 0
\r
236 #define INTERNATIONAL
\r
238 #ifdef INTERNATIONAL
\r
239 # define _(s) T_(s)
\r
245 # define Translate(x, y)
\r
246 # define LoadLanguageFile(s)
\r
249 #ifdef INTERNATIONAL
\r
251 Boolean barbaric; // flag indicating if translation is needed
\r
253 // list of item numbers used in each dialog (used to alter language at run time)
\r
255 #define ABOUTBOX -1 /* not sure why these are needed */
\r
256 #define ABOUTBOX2 -1
\r
258 int dialogItems[][42] = {
\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
261 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
263 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
264 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
265 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
266 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
267 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
268 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
269 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
270 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
271 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
272 { ABOUTBOX2, IDC_ChessBoard },
\r
273 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
274 OPT_GameListClose, IDC_GameListDoFilter },
\r
275 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
276 { DLG_Error, IDOK },
\r
277 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
278 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
279 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
280 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
281 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
282 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
283 { DLG_IndexNumber, IDC_Index },
\r
284 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
285 { DLG_TypeInName, IDOK, IDCANCEL },
\r
286 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
287 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
288 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
289 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
290 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
291 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
292 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
293 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
294 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
295 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
296 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
297 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
298 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
299 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
300 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
301 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
302 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
303 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
304 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
305 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
306 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
307 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
308 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
309 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
310 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
311 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
312 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
313 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
314 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
315 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
316 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
317 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
318 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
319 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
320 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
321 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
322 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
323 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
324 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
325 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
326 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
327 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
328 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
329 { DLG_MoveHistory },
\r
330 { DLG_EvalGraph },
\r
331 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
332 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
333 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
334 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
335 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
336 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
337 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
338 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
339 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
343 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
344 static int lastChecked;
\r
345 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
346 extern int tinyLayout;
\r
347 extern char * menuBarText[][10];
\r
350 LoadLanguageFile(char *name)
\r
351 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
353 int i=0, j=0, n=0, k;
\r
356 if(!name || name[0] == NULLCHAR) return;
\r
357 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
358 appData.language = oldLanguage;
\r
359 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
360 if((f = fopen(buf, "r")) == NULL) return;
\r
361 while((k = fgetc(f)) != EOF) {
\r
362 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
363 languageBuf[i] = k;
\r
365 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
367 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
368 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
369 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
370 english[j] = languageBuf + n + 1; *p = 0;
\r
371 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
372 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
377 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
379 case 'n': k = '\n'; break;
\r
380 case 'r': k = '\r'; break;
\r
381 case 't': k = '\t'; break;
\r
383 languageBuf[--i] = k;
\r
388 barbaric = (j != 0);
\r
389 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
394 { // return the translation of the given string
\r
395 // efficiency can be improved a lot...
\r
397 static char buf[MSG_SIZ];
\r
398 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
399 if(!barbaric) return s;
\r
400 if(!s) return ""; // sanity
\r
401 while(english[i]) {
\r
402 if(!strcmp(s, english[i])) return foreign[i];
\r
403 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
404 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
413 Translate(HWND hDlg, int dialogID)
\r
414 { // translate all text items in the given dialog
\r
416 char buf[MSG_SIZ], *s;
\r
417 if(!barbaric) return;
\r
418 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
419 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
420 GetWindowText( hDlg, buf, MSG_SIZ );
\r
422 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
423 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
424 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
425 if(strlen(buf) == 0) continue;
\r
427 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
432 TranslateOneMenu(int i, HMENU subMenu)
\r
435 static MENUITEMINFO info;
\r
437 info.cbSize = sizeof(MENUITEMINFO);
\r
438 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
439 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
441 info.dwTypeData = buf;
\r
442 info.cch = sizeof(buf);
\r
443 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
445 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
446 else menuText[i][j] = strdup(buf); // remember original on first change
\r
448 if(buf[0] == NULLCHAR) continue;
\r
449 info.dwTypeData = T_(buf);
\r
450 info.cch = strlen(buf)+1;
\r
451 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
457 TranslateMenus(int addLanguage)
\r
460 WIN32_FIND_DATA fileData;
\r
462 #define IDM_English 1970
\r
464 HMENU mainMenu = GetMenu(hwndMain);
\r
465 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
466 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
467 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
468 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
469 TranslateOneMenu(i, subMenu);
\r
471 DrawMenuBar(hwndMain);
\r
474 if(!addLanguage) return;
\r
475 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
476 HMENU mainMenu = GetMenu(hwndMain);
\r
477 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
478 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
479 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
480 i = 0; lastChecked = IDM_English;
\r
482 char *p, *q = fileData.cFileName;
\r
483 int checkFlag = MF_UNCHECKED;
\r
484 languageFile[i] = strdup(q);
\r
485 if(barbaric && !strcmp(oldLanguage, q)) {
\r
486 checkFlag = MF_CHECKED;
\r
487 lastChecked = IDM_English + i + 1;
\r
488 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
490 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
491 p = strstr(fileData.cFileName, ".lng");
\r
493 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
494 } while(FindNextFile(hFind, &fileData));
\r
501 #define IDM_RecentEngines 3000
\r
504 RecentEngineMenu (char *s)
\r
506 if(appData.icsActive) return;
\r
507 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
508 HMENU mainMenu = GetMenu(hwndMain);
\r
509 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
510 int i=IDM_RecentEngines;
\r
511 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
512 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
514 char *p = strchr(s, '\n');
\r
515 if(p == NULL) return; // malformed!
\r
517 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
531 int cliWidth, cliHeight;
\r
534 SizeInfo sizeInfo[] =
\r
536 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
537 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
538 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
539 { "petite", 33, 1, 1, 1, 0, 0 },
\r
540 { "slim", 37, 2, 1, 0, 0, 0 },
\r
541 { "small", 40, 2, 1, 0, 0, 0 },
\r
542 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
543 { "middling", 49, 2, 0, 0, 0, 0 },
\r
544 { "average", 54, 2, 0, 0, 0, 0 },
\r
545 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
546 { "medium", 64, 3, 0, 0, 0, 0 },
\r
547 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
548 { "large", 80, 3, 0, 0, 0, 0 },
\r
549 { "big", 87, 3, 0, 0, 0, 0 },
\r
550 { "huge", 95, 3, 0, 0, 0, 0 },
\r
551 { "giant", 108, 3, 0, 0, 0, 0 },
\r
552 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
553 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
554 { NULL, 0, 0, 0, 0, 0, 0 }
\r
557 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
558 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
577 { 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
580 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
589 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
590 #define N_BUTTONS 5
\r
592 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
594 {"<<", IDM_ToStart, NULL, NULL},
\r
595 {"<", IDM_Backward, NULL, NULL},
\r
596 {"P", IDM_Pause, NULL, NULL},
\r
597 {">", IDM_Forward, NULL, NULL},
\r
598 {">>", IDM_ToEnd, NULL, NULL},
\r
601 int tinyLayout = 0, smallLayout = 0;
\r
602 #define MENU_BAR_ITEMS 9
\r
603 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
604 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
605 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
609 MySound sounds[(int)NSoundClasses];
\r
610 MyTextAttribs textAttribs[(int)NColorClasses];
\r
612 MyColorizeAttribs colorizeAttribs[] = {
\r
613 { (COLORREF)0, 0, N_("Shout Text") },
\r
614 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
615 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
616 { (COLORREF)0, 0, N_("Channel Text") },
\r
617 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
618 { (COLORREF)0, 0, N_("Tell Text") },
\r
619 { (COLORREF)0, 0, N_("Challenge Text") },
\r
620 { (COLORREF)0, 0, N_("Request Text") },
\r
621 { (COLORREF)0, 0, N_("Seek Text") },
\r
622 { (COLORREF)0, 0, N_("Normal Text") },
\r
623 { (COLORREF)0, 0, N_("None") }
\r
628 static char *commentTitle;
\r
629 static char *commentText;
\r
630 static int commentIndex;
\r
631 static Boolean editComment = FALSE;
\r
634 char errorTitle[MSG_SIZ];
\r
635 char errorMessage[2*MSG_SIZ];
\r
636 HWND errorDialog = NULL;
\r
637 BOOLEAN moveErrorMessageUp = FALSE;
\r
638 BOOLEAN consoleEcho = TRUE;
\r
639 CHARFORMAT consoleCF;
\r
640 COLORREF consoleBackgroundColor;
\r
642 char *programVersion;
\r
648 typedef int CPKind;
\r
657 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
660 #define INPUT_SOURCE_BUF_SIZE 4096
\r
662 typedef struct _InputSource {
\r
669 char buf[INPUT_SOURCE_BUF_SIZE];
\r
673 InputCallback func;
\r
674 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
678 InputSource *consoleInputSource;
\r
683 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
684 VOID ConsoleCreate();
\r
686 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
687 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
688 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
689 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
691 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
692 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
693 void ParseIcsTextMenu(char *icsTextMenuString);
\r
694 VOID PopUpNameDialog(char firstchar);
\r
695 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
699 int GameListOptions();
\r
701 int dummy; // [HGM] for obsolete args
\r
703 HWND hwndMain = NULL; /* root window*/
\r
704 HWND hwndConsole = NULL;
\r
705 HWND commentDialog = NULL;
\r
706 HWND moveHistoryDialog = NULL;
\r
707 HWND evalGraphDialog = NULL;
\r
708 HWND engineOutputDialog = NULL;
\r
709 HWND gameListDialog = NULL;
\r
710 HWND editTagsDialog = NULL;
\r
712 int commentUp = FALSE;
\r
714 WindowPlacement wpMain;
\r
715 WindowPlacement wpConsole;
\r
716 WindowPlacement wpComment;
\r
717 WindowPlacement wpMoveHistory;
\r
718 WindowPlacement wpEvalGraph;
\r
719 WindowPlacement wpEngineOutput;
\r
720 WindowPlacement wpGameList;
\r
721 WindowPlacement wpTags;
\r
723 VOID EngineOptionsPopup(); // [HGM] settings
\r
725 VOID GothicPopUp(char *title, VariantClass variant);
\r
727 * Setting "frozen" should disable all user input other than deleting
\r
728 * the window. We do this while engines are initializing themselves.
\r
730 static int frozen = 0;
\r
731 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
737 if (frozen) return;
\r
739 hmenu = GetMenu(hwndMain);
\r
740 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
741 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
743 DrawMenuBar(hwndMain);
\r
746 /* Undo a FreezeUI */
\r
752 if (!frozen) return;
\r
754 hmenu = GetMenu(hwndMain);
\r
755 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
756 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
758 DrawMenuBar(hwndMain);
\r
761 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
763 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
769 #define JAWS_ALT_INTERCEPT
\r
770 #define JAWS_KBUP_NAVIGATION
\r
771 #define JAWS_KBDOWN_NAVIGATION
\r
772 #define JAWS_MENU_ITEMS
\r
773 #define JAWS_SILENCE
\r
774 #define JAWS_REPLAY
\r
776 #define JAWS_COPYRIGHT
\r
777 #define JAWS_DELETE(X) X
\r
778 #define SAYMACHINEMOVE()
\r
782 /*---------------------------------------------------------------------------*\
\r
786 \*---------------------------------------------------------------------------*/
\r
789 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
790 LPSTR lpCmdLine, int nCmdShow)
\r
793 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
794 // INITCOMMONCONTROLSEX ex;
\r
798 LoadLibrary("RICHED32.DLL");
\r
799 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
801 if (!InitApplication(hInstance)) {
\r
804 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
811 // InitCommonControlsEx(&ex);
\r
812 InitCommonControls();
\r
814 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
815 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
816 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
818 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
820 while (GetMessage(&msg, /* message structure */
\r
821 NULL, /* handle of window receiving the message */
\r
822 0, /* lowest message to examine */
\r
823 0)) /* highest message to examine */
\r
826 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
827 // [HGM] navigate: switch between all windows with tab
\r
828 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
829 int i, currentElement = 0;
\r
831 // first determine what element of the chain we come from (if any)
\r
832 if(appData.icsActive) {
\r
833 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
834 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
836 if(engineOutputDialog && EngineOutputIsUp()) {
\r
837 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
838 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
840 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
841 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
843 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
844 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
845 if(msg.hwnd == e1) currentElement = 2; else
\r
846 if(msg.hwnd == e2) currentElement = 3; else
\r
847 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
848 if(msg.hwnd == mh) currentElement = 4; else
\r
849 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
850 if(msg.hwnd == hText) currentElement = 5; else
\r
851 if(msg.hwnd == hInput) currentElement = 6; else
\r
852 for (i = 0; i < N_BUTTONS; i++) {
\r
853 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
856 // determine where to go to
\r
857 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
859 currentElement = (currentElement + direction) % 7;
\r
860 switch(currentElement) {
\r
862 h = hwndMain; break; // passing this case always makes the loop exit
\r
864 h = buttonDesc[0].hwnd; break; // could be NULL
\r
866 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
869 if(!EngineOutputIsUp()) continue;
\r
872 if(!MoveHistoryIsUp()) continue;
\r
874 // case 6: // input to eval graph does not seem to get here!
\r
875 // if(!EvalGraphIsUp()) continue;
\r
876 // h = evalGraphDialog; break;
\r
878 if(!appData.icsActive) continue;
\r
882 if(!appData.icsActive) continue;
\r
888 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
889 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
892 continue; // this message now has been processed
\r
896 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
897 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
898 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
899 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
900 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
901 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
902 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
903 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
904 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
905 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
906 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
907 for(i=0; i<MAX_CHAT; i++)
\r
908 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
911 if(done) continue; // [HGM] chat: end patch
\r
912 TranslateMessage(&msg); /* Translates virtual key codes */
\r
913 DispatchMessage(&msg); /* Dispatches message to window */
\r
918 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
921 /*---------------------------------------------------------------------------*\
\r
923 * Initialization functions
\r
925 \*---------------------------------------------------------------------------*/
\r
929 { // update user logo if necessary
\r
930 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
932 if(appData.autoLogo) {
\r
933 curName = UserName();
\r
934 if(strcmp(curName, oldUserName)) {
\r
935 GetCurrentDirectory(MSG_SIZ, dir);
\r
936 SetCurrentDirectory(installDir);
\r
937 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
938 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
939 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
940 if(userLogo == NULL)
\r
941 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
942 SetCurrentDirectory(dir); /* return to prev directory */
\r
948 InitApplication(HINSTANCE hInstance)
\r
952 /* Fill in window class structure with parameters that describe the */
\r
955 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
956 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
957 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
958 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
959 wc.hInstance = hInstance; /* Owner of this class */
\r
960 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
961 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
962 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
963 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
964 wc.lpszClassName = szAppName; /* Name to register as */
\r
966 /* Register the window class and return success/failure code. */
\r
967 if (!RegisterClass(&wc)) return FALSE;
\r
969 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
970 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
972 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
973 wc.hInstance = hInstance;
\r
974 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
975 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
976 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
977 wc.lpszMenuName = NULL;
\r
978 wc.lpszClassName = szConsoleName;
\r
980 if (!RegisterClass(&wc)) return FALSE;
\r
985 /* Set by InitInstance, used by EnsureOnScreen */
\r
986 int screenHeight, screenWidth;
\r
987 RECT screenGeometry;
\r
990 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
992 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
993 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
994 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
995 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
996 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
997 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1001 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1003 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1004 GetCurrentDirectory(MSG_SIZ, dir);
\r
1005 SetCurrentDirectory(installDir);
\r
1006 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1007 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1009 if (cps->programLogo == NULL && appData.debugMode) {
\r
1010 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1012 } else if(appData.autoLogo) {
\r
1013 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1014 char *opponent = "";
\r
1015 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1016 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1017 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1018 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1019 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1020 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1023 if(appData.directory[n] && appData.directory[n][0]) {
\r
1024 SetCurrentDirectory(appData.directory[n]);
\r
1025 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1028 SetCurrentDirectory(dir); /* return to prev directory */
\r
1034 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1035 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1037 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1038 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1039 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1040 liteBackTextureMode = appData.liteBackTextureMode;
\r
1042 if (liteBackTexture == NULL && appData.debugMode) {
\r
1043 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1047 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1048 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1049 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1050 darkBackTextureMode = appData.darkBackTextureMode;
\r
1052 if (darkBackTexture == NULL && appData.debugMode) {
\r
1053 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1058 #ifndef SM_CXVIRTUALSCREEN
\r
1059 #define SM_CXVIRTUALSCREEN 78
\r
1061 #ifndef SM_CYVIRTUALSCREEN
\r
1062 #define SM_CYVIRTUALSCREEN 79
\r
1064 #ifndef SM_XVIRTUALSCREEN
\r
1065 #define SM_XVIRTUALSCREEN 76
\r
1067 #ifndef SM_YVIRTUALSCREEN
\r
1068 #define SM_YVIRTUALSCREEN 77
\r
1074 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1075 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1076 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1077 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1078 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1079 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1080 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1081 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1085 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1087 HWND hwnd; /* Main window handle. */
\r
1089 WINDOWPLACEMENT wp;
\r
1092 hInst = hInstance; /* Store instance handle in our global variable */
\r
1093 programName = szAppName;
\r
1095 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1096 *filepart = NULLCHAR;
\r
1097 SetCurrentDirectory(installDir);
\r
1099 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1101 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1103 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1104 /* xboard, and older WinBoards, controlled the move sound with the
\r
1105 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1106 always turn the option on (so that the backend will call us),
\r
1107 then let the user turn the sound off by setting it to silence if
\r
1108 desired. To accommodate old winboard.ini files saved by old
\r
1109 versions of WinBoard, we also turn off the sound if the option
\r
1110 was initially set to false. [HGM] taken out of InitAppData */
\r
1111 if (!appData.ringBellAfterMoves) {
\r
1112 sounds[(int)SoundMove].name = strdup("");
\r
1113 appData.ringBellAfterMoves = TRUE;
\r
1115 if (appData.debugMode) {
\r
1116 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1117 setbuf(debugFP, NULL);
\r
1120 LoadLanguageFile(appData.language);
\r
1124 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1125 // InitEngineUCI( installDir, &second );
\r
1127 /* Create a main window for this application instance. */
\r
1128 hwnd = CreateWindow(szAppName, szTitle,
\r
1129 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1130 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1131 NULL, NULL, hInstance, NULL);
\r
1134 /* If window could not be created, return "failure" */
\r
1139 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1140 LoadLogo(&first, 0, FALSE);
\r
1141 LoadLogo(&second, 1, appData.icsActive);
\r
1145 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1146 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1147 iconCurrent = iconWhite;
\r
1148 InitDrawingColors();
\r
1150 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1151 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1152 /* Compute window size for each board size, and use the largest
\r
1153 size that fits on this screen as the default. */
\r
1154 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1155 if (boardSize == (BoardSize)-1 &&
\r
1156 winH <= screenHeight
\r
1157 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1158 && winW <= screenWidth) {
\r
1159 boardSize = (BoardSize)ibs;
\r
1163 InitDrawingSizes(boardSize, 0);
\r
1164 RecentEngineMenu(appData.recentEngineList);
\r
1166 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1168 /* [AS] Load textures if specified */
\r
1171 mysrandom( (unsigned) time(NULL) );
\r
1173 /* [AS] Restore layout */
\r
1174 if( wpMoveHistory.visible ) {
\r
1175 MoveHistoryPopUp();
\r
1178 if( wpEvalGraph.visible ) {
\r
1182 if( wpEngineOutput.visible ) {
\r
1183 EngineOutputPopUp();
\r
1186 /* Make the window visible; update its client area; and return "success" */
\r
1187 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1188 wp.length = sizeof(WINDOWPLACEMENT);
\r
1190 wp.showCmd = nCmdShow;
\r
1191 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1192 wp.rcNormalPosition.left = wpMain.x;
\r
1193 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1194 wp.rcNormalPosition.top = wpMain.y;
\r
1195 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1196 SetWindowPlacement(hwndMain, &wp);
\r
1198 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1200 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1201 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1203 if (hwndConsole) {
\r
1205 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1206 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1208 ShowWindow(hwndConsole, nCmdShow);
\r
1209 SetActiveWindow(hwndConsole);
\r
1211 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1212 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1221 HMENU hmenu = GetMenu(hwndMain);
\r
1223 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1224 MF_BYCOMMAND|((appData.icsActive &&
\r
1225 *appData.icsCommPort != NULLCHAR) ?
\r
1226 MF_ENABLED : MF_GRAYED));
\r
1227 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1228 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1229 MF_CHECKED : MF_UNCHECKED));
\r
1232 //---------------------------------------------------------------------------------------------------------
\r
1234 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1235 #define XBOARD FALSE
\r
1237 #define OPTCHAR "/"
\r
1238 #define SEPCHAR "="
\r
1239 #define TOPLEVEL 0
\r
1243 // front-end part of option handling
\r
1246 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1248 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1249 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1252 lf->lfEscapement = 0;
\r
1253 lf->lfOrientation = 0;
\r
1254 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1255 lf->lfItalic = mfp->italic;
\r
1256 lf->lfUnderline = mfp->underline;
\r
1257 lf->lfStrikeOut = mfp->strikeout;
\r
1258 lf->lfCharSet = mfp->charset;
\r
1259 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1260 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1261 lf->lfQuality = DEFAULT_QUALITY;
\r
1262 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1263 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1267 CreateFontInMF(MyFont *mf)
\r
1269 LFfromMFP(&mf->lf, &mf->mfp);
\r
1270 if (mf->hf) DeleteObject(mf->hf);
\r
1271 mf->hf = CreateFontIndirect(&mf->lf);
\r
1274 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1276 colorVariable[] = {
\r
1277 &whitePieceColor,
\r
1278 &blackPieceColor,
\r
1279 &lightSquareColor,
\r
1280 &darkSquareColor,
\r
1281 &highlightSquareColor,
\r
1282 &premoveHighlightColor,
\r
1284 &consoleBackgroundColor,
\r
1285 &appData.fontForeColorWhite,
\r
1286 &appData.fontBackColorWhite,
\r
1287 &appData.fontForeColorBlack,
\r
1288 &appData.fontBackColorBlack,
\r
1289 &appData.evalHistColorWhite,
\r
1290 &appData.evalHistColorBlack,
\r
1291 &appData.highlightArrowColor,
\r
1294 /* Command line font name parser. NULL name means do nothing.
\r
1295 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1296 For backward compatibility, syntax without the colon is also
\r
1297 accepted, but font names with digits in them won't work in that case.
\r
1300 ParseFontName(char *name, MyFontParams *mfp)
\r
1303 if (name == NULL) return;
\r
1305 q = strchr(p, ':');
\r
1307 if (q - p >= sizeof(mfp->faceName))
\r
1308 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1309 memcpy(mfp->faceName, p, q - p);
\r
1310 mfp->faceName[q - p] = NULLCHAR;
\r
1313 q = mfp->faceName;
\r
1315 while (*p && !isdigit(*p)) {
\r
1317 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1318 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1320 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1323 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1324 mfp->pointSize = (float) atof(p);
\r
1325 mfp->bold = (strchr(p, 'b') != NULL);
\r
1326 mfp->italic = (strchr(p, 'i') != NULL);
\r
1327 mfp->underline = (strchr(p, 'u') != NULL);
\r
1328 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1329 mfp->charset = DEFAULT_CHARSET;
\r
1330 q = strchr(p, 'c');
\r
1332 mfp->charset = (BYTE) atoi(q+1);
\r
1336 ParseFont(char *name, int number)
\r
1337 { // wrapper to shield back-end from 'font'
\r
1338 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1343 { // in WB we have a 2D array of fonts; this initializes their description
\r
1345 /* Point font array elements to structures and
\r
1346 parse default font names */
\r
1347 for (i=0; i<NUM_FONTS; i++) {
\r
1348 for (j=0; j<NUM_SIZES; j++) {
\r
1349 font[j][i] = &fontRec[j][i];
\r
1350 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1357 { // here we create the actual fonts from the selected descriptions
\r
1359 for (i=0; i<NUM_FONTS; i++) {
\r
1360 for (j=0; j<NUM_SIZES; j++) {
\r
1361 CreateFontInMF(font[j][i]);
\r
1365 /* Color name parser.
\r
1366 X version accepts X color names, but this one
\r
1367 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1369 ParseColorName(char *name)
\r
1371 int red, green, blue, count;
\r
1372 char buf[MSG_SIZ];
\r
1374 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1376 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1377 &red, &green, &blue);
\r
1380 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1381 DisplayError(buf, 0);
\r
1382 return RGB(0, 0, 0);
\r
1384 return PALETTERGB(red, green, blue);
\r
1388 ParseColor(int n, char *name)
\r
1389 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1390 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1394 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1396 char *e = argValue;
\r
1400 if (*e == 'b') eff |= CFE_BOLD;
\r
1401 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1402 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1403 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1404 else if (*e == '#' || isdigit(*e)) break;
\r
1408 *color = ParseColorName(e);
\r
1412 ParseTextAttribs(ColorClass cc, char *s)
\r
1413 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1414 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1415 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1419 ParseBoardSize(void *addr, char *name)
\r
1420 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1421 BoardSize bs = SizeTiny;
\r
1422 while (sizeInfo[bs].name != NULL) {
\r
1423 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1424 *(BoardSize *)addr = bs;
\r
1429 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1434 { // [HGM] import name from appData first
\r
1437 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1438 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1439 textAttribs[cc].sound.data = NULL;
\r
1440 MyLoadSound(&textAttribs[cc].sound);
\r
1442 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1443 textAttribs[cc].sound.name = strdup("");
\r
1444 textAttribs[cc].sound.data = NULL;
\r
1446 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1447 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1448 sounds[sc].data = NULL;
\r
1449 MyLoadSound(&sounds[sc]);
\r
1454 SetCommPortDefaults()
\r
1456 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1457 dcb.DCBlength = sizeof(DCB);
\r
1458 dcb.BaudRate = 9600;
\r
1459 dcb.fBinary = TRUE;
\r
1460 dcb.fParity = FALSE;
\r
1461 dcb.fOutxCtsFlow = FALSE;
\r
1462 dcb.fOutxDsrFlow = FALSE;
\r
1463 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1464 dcb.fDsrSensitivity = FALSE;
\r
1465 dcb.fTXContinueOnXoff = TRUE;
\r
1466 dcb.fOutX = FALSE;
\r
1468 dcb.fNull = FALSE;
\r
1469 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1470 dcb.fAbortOnError = FALSE;
\r
1472 dcb.Parity = SPACEPARITY;
\r
1473 dcb.StopBits = ONESTOPBIT;
\r
1476 // [HGM] args: these three cases taken out to stay in front-end
\r
1478 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1479 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1480 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1481 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1483 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1484 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1485 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1486 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1487 ad->argName, mfp->faceName, mfp->pointSize,
\r
1488 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1489 mfp->bold ? "b" : "",
\r
1490 mfp->italic ? "i" : "",
\r
1491 mfp->underline ? "u" : "",
\r
1492 mfp->strikeout ? "s" : "",
\r
1493 (int)mfp->charset);
\r
1499 { // [HGM] copy the names from the internal WB variables to appData
\r
1502 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1503 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1504 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1505 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1509 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1510 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1511 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1512 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1513 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1514 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1515 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1516 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1517 (ta->effects) ? " " : "",
\r
1518 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1522 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1523 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1524 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1525 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1526 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1530 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1531 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1532 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1536 ParseCommPortSettings(char *s)
\r
1537 { // wrapper to keep dcb from back-end
\r
1538 ParseCommSettings(s, &dcb);
\r
1543 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1544 GetActualPlacement(hwndMain, &wpMain);
\r
1545 GetActualPlacement(hwndConsole, &wpConsole);
\r
1546 GetActualPlacement(commentDialog, &wpComment);
\r
1547 GetActualPlacement(editTagsDialog, &wpTags);
\r
1548 GetActualPlacement(gameListDialog, &wpGameList);
\r
1549 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1550 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1551 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1555 PrintCommPortSettings(FILE *f, char *name)
\r
1556 { // wrapper to shield back-end from DCB
\r
1557 PrintCommSettings(f, name, &dcb);
\r
1561 MySearchPath(char *installDir, char *name, char *fullname)
\r
1563 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1564 if(name[0]== '%') {
\r
1565 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1566 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1567 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1568 *strchr(buf, '%') = 0;
\r
1569 strcat(fullname, getenv(buf));
\r
1570 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1572 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1573 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1574 return (int) strlen(fullname);
\r
1576 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1580 MyGetFullPathName(char *name, char *fullname)
\r
1583 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1588 { // [HGM] args: allows testing if main window is realized from back-end
\r
1589 return hwndMain != NULL;
\r
1593 PopUpStartupDialog()
\r
1597 LoadLanguageFile(appData.language);
\r
1598 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1599 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1600 FreeProcInstance(lpProc);
\r
1603 /*---------------------------------------------------------------------------*\
\r
1605 * GDI board drawing routines
\r
1607 \*---------------------------------------------------------------------------*/
\r
1609 /* [AS] Draw square using background texture */
\r
1610 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1615 return; /* Should never happen! */
\r
1618 SetGraphicsMode( dst, GM_ADVANCED );
\r
1625 /* X reflection */
\r
1630 x.eDx = (FLOAT) dw + dx - 1;
\r
1633 SetWorldTransform( dst, &x );
\r
1636 /* Y reflection */
\r
1642 x.eDy = (FLOAT) dh + dy - 1;
\r
1644 SetWorldTransform( dst, &x );
\r
1652 x.eDx = (FLOAT) dx;
\r
1653 x.eDy = (FLOAT) dy;
\r
1656 SetWorldTransform( dst, &x );
\r
1660 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1668 SetWorldTransform( dst, &x );
\r
1670 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1673 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1675 PM_WP = (int) WhitePawn,
\r
1676 PM_WN = (int) WhiteKnight,
\r
1677 PM_WB = (int) WhiteBishop,
\r
1678 PM_WR = (int) WhiteRook,
\r
1679 PM_WQ = (int) WhiteQueen,
\r
1680 PM_WF = (int) WhiteFerz,
\r
1681 PM_WW = (int) WhiteWazir,
\r
1682 PM_WE = (int) WhiteAlfil,
\r
1683 PM_WM = (int) WhiteMan,
\r
1684 PM_WO = (int) WhiteCannon,
\r
1685 PM_WU = (int) WhiteUnicorn,
\r
1686 PM_WH = (int) WhiteNightrider,
\r
1687 PM_WA = (int) WhiteAngel,
\r
1688 PM_WC = (int) WhiteMarshall,
\r
1689 PM_WAB = (int) WhiteCardinal,
\r
1690 PM_WD = (int) WhiteDragon,
\r
1691 PM_WL = (int) WhiteLance,
\r
1692 PM_WS = (int) WhiteCobra,
\r
1693 PM_WV = (int) WhiteFalcon,
\r
1694 PM_WSG = (int) WhiteSilver,
\r
1695 PM_WG = (int) WhiteGrasshopper,
\r
1696 PM_WK = (int) WhiteKing,
\r
1697 PM_BP = (int) BlackPawn,
\r
1698 PM_BN = (int) BlackKnight,
\r
1699 PM_BB = (int) BlackBishop,
\r
1700 PM_BR = (int) BlackRook,
\r
1701 PM_BQ = (int) BlackQueen,
\r
1702 PM_BF = (int) BlackFerz,
\r
1703 PM_BW = (int) BlackWazir,
\r
1704 PM_BE = (int) BlackAlfil,
\r
1705 PM_BM = (int) BlackMan,
\r
1706 PM_BO = (int) BlackCannon,
\r
1707 PM_BU = (int) BlackUnicorn,
\r
1708 PM_BH = (int) BlackNightrider,
\r
1709 PM_BA = (int) BlackAngel,
\r
1710 PM_BC = (int) BlackMarshall,
\r
1711 PM_BG = (int) BlackGrasshopper,
\r
1712 PM_BAB = (int) BlackCardinal,
\r
1713 PM_BD = (int) BlackDragon,
\r
1714 PM_BL = (int) BlackLance,
\r
1715 PM_BS = (int) BlackCobra,
\r
1716 PM_BV = (int) BlackFalcon,
\r
1717 PM_BSG = (int) BlackSilver,
\r
1718 PM_BK = (int) BlackKing
\r
1721 static HFONT hPieceFont = NULL;
\r
1722 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1723 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1724 static int fontBitmapSquareSize = 0;
\r
1725 static char pieceToFontChar[(int) EmptySquare] =
\r
1726 { 'p', 'n', 'b', 'r', 'q',
\r
1727 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1728 'k', 'o', 'm', 'v', 't', 'w',
\r
1729 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1732 extern BOOL SetCharTable( char *table, const char * map );
\r
1733 /* [HGM] moved to backend.c */
\r
1735 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1738 BYTE r1 = GetRValue( color );
\r
1739 BYTE g1 = GetGValue( color );
\r
1740 BYTE b1 = GetBValue( color );
\r
1746 /* Create a uniform background first */
\r
1747 hbrush = CreateSolidBrush( color );
\r
1748 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1749 FillRect( hdc, &rc, hbrush );
\r
1750 DeleteObject( hbrush );
\r
1753 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1754 int steps = squareSize / 2;
\r
1757 for( i=0; i<steps; i++ ) {
\r
1758 BYTE r = r1 - (r1-r2) * i / steps;
\r
1759 BYTE g = g1 - (g1-g2) * i / steps;
\r
1760 BYTE b = b1 - (b1-b2) * i / steps;
\r
1762 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1763 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1764 FillRect( hdc, &rc, hbrush );
\r
1765 DeleteObject(hbrush);
\r
1768 else if( mode == 2 ) {
\r
1769 /* Diagonal gradient, good more or less for every piece */
\r
1770 POINT triangle[3];
\r
1771 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1772 HBRUSH hbrush_old;
\r
1773 int steps = squareSize;
\r
1776 triangle[0].x = squareSize - steps;
\r
1777 triangle[0].y = squareSize;
\r
1778 triangle[1].x = squareSize;
\r
1779 triangle[1].y = squareSize;
\r
1780 triangle[2].x = squareSize;
\r
1781 triangle[2].y = squareSize - steps;
\r
1783 for( i=0; i<steps; i++ ) {
\r
1784 BYTE r = r1 - (r1-r2) * i / steps;
\r
1785 BYTE g = g1 - (g1-g2) * i / steps;
\r
1786 BYTE b = b1 - (b1-b2) * i / steps;
\r
1788 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1789 hbrush_old = SelectObject( hdc, hbrush );
\r
1790 Polygon( hdc, triangle, 3 );
\r
1791 SelectObject( hdc, hbrush_old );
\r
1792 DeleteObject(hbrush);
\r
1797 SelectObject( hdc, hpen );
\r
1802 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1803 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1804 piece: follow the steps as explained below.
\r
1806 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1810 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1815 int backColor = whitePieceColor;
\r
1816 int foreColor = blackPieceColor;
\r
1818 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1819 backColor = appData.fontBackColorWhite;
\r
1820 foreColor = appData.fontForeColorWhite;
\r
1822 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1823 backColor = appData.fontBackColorBlack;
\r
1824 foreColor = appData.fontForeColorBlack;
\r
1828 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1830 hbm_old = SelectObject( hdc, hbm );
\r
1834 rc.right = squareSize;
\r
1835 rc.bottom = squareSize;
\r
1837 /* Step 1: background is now black */
\r
1838 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1840 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1842 pt.x = (squareSize - sz.cx) / 2;
\r
1843 pt.y = (squareSize - sz.cy) / 2;
\r
1845 SetBkMode( hdc, TRANSPARENT );
\r
1846 SetTextColor( hdc, chroma );
\r
1847 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1848 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1850 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1851 /* Step 3: the area outside the piece is filled with white */
\r
1852 // FloodFill( hdc, 0, 0, chroma );
\r
1853 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1854 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1855 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1856 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1857 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1859 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1860 but if the start point is not inside the piece we're lost!
\r
1861 There should be a better way to do this... if we could create a region or path
\r
1862 from the fill operation we would be fine for example.
\r
1864 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1865 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1867 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1868 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1869 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1871 SelectObject( dc2, bm2 );
\r
1872 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1873 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1874 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1875 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1876 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1879 DeleteObject( bm2 );
\r
1882 SetTextColor( hdc, 0 );
\r
1884 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1885 draw the piece again in black for safety.
\r
1887 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1889 SelectObject( hdc, hbm_old );
\r
1891 if( hPieceMask[index] != NULL ) {
\r
1892 DeleteObject( hPieceMask[index] );
\r
1895 hPieceMask[index] = hbm;
\r
1898 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1900 SelectObject( hdc, hbm );
\r
1903 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1904 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1905 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1907 SelectObject( dc1, hPieceMask[index] );
\r
1908 SelectObject( dc2, bm2 );
\r
1909 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1910 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1913 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1914 the piece background and deletes (makes transparent) the rest.
\r
1915 Thanks to that mask, we are free to paint the background with the greates
\r
1916 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1917 We use this, to make gradients and give the pieces a "roundish" look.
\r
1919 SetPieceBackground( hdc, backColor, 2 );
\r
1920 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1924 DeleteObject( bm2 );
\r
1927 SetTextColor( hdc, foreColor );
\r
1928 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1930 SelectObject( hdc, hbm_old );
\r
1932 if( hPieceFace[index] != NULL ) {
\r
1933 DeleteObject( hPieceFace[index] );
\r
1936 hPieceFace[index] = hbm;
\r
1939 static int TranslatePieceToFontPiece( int piece )
\r
1969 case BlackMarshall:
\r
1973 case BlackNightrider:
\r
1979 case BlackUnicorn:
\r
1983 case BlackGrasshopper:
\r
1995 case BlackCardinal:
\r
2002 case WhiteMarshall:
\r
2006 case WhiteNightrider:
\r
2012 case WhiteUnicorn:
\r
2016 case WhiteGrasshopper:
\r
2028 case WhiteCardinal:
\r
2037 void CreatePiecesFromFont()
\r
2040 HDC hdc_window = NULL;
\r
2046 if( fontBitmapSquareSize < 0 ) {
\r
2047 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2051 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2052 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2053 fontBitmapSquareSize = -1;
\r
2057 if( fontBitmapSquareSize != squareSize ) {
\r
2058 hdc_window = GetDC( hwndMain );
\r
2059 hdc = CreateCompatibleDC( hdc_window );
\r
2061 if( hPieceFont != NULL ) {
\r
2062 DeleteObject( hPieceFont );
\r
2065 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2066 hPieceMask[i] = NULL;
\r
2067 hPieceFace[i] = NULL;
\r
2073 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2074 fontHeight = appData.fontPieceSize;
\r
2077 fontHeight = (fontHeight * squareSize) / 100;
\r
2079 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2081 lf.lfEscapement = 0;
\r
2082 lf.lfOrientation = 0;
\r
2083 lf.lfWeight = FW_NORMAL;
\r
2085 lf.lfUnderline = 0;
\r
2086 lf.lfStrikeOut = 0;
\r
2087 lf.lfCharSet = DEFAULT_CHARSET;
\r
2088 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2089 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2090 lf.lfQuality = PROOF_QUALITY;
\r
2091 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2092 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2093 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2095 hPieceFont = CreateFontIndirect( &lf );
\r
2097 if( hPieceFont == NULL ) {
\r
2098 fontBitmapSquareSize = -2;
\r
2101 /* Setup font-to-piece character table */
\r
2102 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2103 /* No (or wrong) global settings, try to detect the font */
\r
2104 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2106 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2108 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2109 /* DiagramTT* family */
\r
2110 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2112 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2113 /* Fairy symbols */
\r
2114 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2116 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2117 /* Good Companion (Some characters get warped as literal :-( */
\r
2118 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2119 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2120 SetCharTable(pieceToFontChar, s);
\r
2123 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2124 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2128 /* Create bitmaps */
\r
2129 hfont_old = SelectObject( hdc, hPieceFont );
\r
2130 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2131 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2132 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2134 SelectObject( hdc, hfont_old );
\r
2136 fontBitmapSquareSize = squareSize;
\r
2140 if( hdc != NULL ) {
\r
2144 if( hdc_window != NULL ) {
\r
2145 ReleaseDC( hwndMain, hdc_window );
\r
2150 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2152 char name[128], buf[MSG_SIZ];
\r
2154 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2155 if(appData.pieceDirectory[0]) {
\r
2157 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2158 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2159 if(res) return res;
\r
2161 if (gameInfo.event &&
\r
2162 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2163 strcmp(name, "k80s") == 0) {
\r
2164 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2166 return LoadBitmap(hinst, name);
\r
2170 /* Insert a color into the program's logical palette
\r
2171 structure. This code assumes the given color is
\r
2172 the result of the RGB or PALETTERGB macro, and it
\r
2173 knows how those macros work (which is documented).
\r
2176 InsertInPalette(COLORREF color)
\r
2178 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2180 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2181 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2182 pLogPal->palNumEntries--;
\r
2186 pe->peFlags = (char) 0;
\r
2187 pe->peRed = (char) (0xFF & color);
\r
2188 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2189 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2195 InitDrawingColors()
\r
2198 if (pLogPal == NULL) {
\r
2199 /* Allocate enough memory for a logical palette with
\r
2200 * PALETTESIZE entries and set the size and version fields
\r
2201 * of the logical palette structure.
\r
2203 pLogPal = (NPLOGPALETTE)
\r
2204 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2205 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2206 pLogPal->palVersion = 0x300;
\r
2208 pLogPal->palNumEntries = 0;
\r
2210 InsertInPalette(lightSquareColor);
\r
2211 InsertInPalette(darkSquareColor);
\r
2212 InsertInPalette(whitePieceColor);
\r
2213 InsertInPalette(blackPieceColor);
\r
2214 InsertInPalette(highlightSquareColor);
\r
2215 InsertInPalette(premoveHighlightColor);
\r
2217 /* create a logical color palette according the information
\r
2218 * in the LOGPALETTE structure.
\r
2220 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2222 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2223 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2224 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2225 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2226 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2227 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2228 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2229 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2231 /* [AS] Force rendering of the font-based pieces */
\r
2232 if( fontBitmapSquareSize > 0 ) {
\r
2233 fontBitmapSquareSize = 0;
\r
2239 BoardWidth(int boardSize, int n)
\r
2240 { /* [HGM] argument n added to allow different width and height */
\r
2241 int lineGap = sizeInfo[boardSize].lineGap;
\r
2243 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2244 lineGap = appData.overrideLineGap;
\r
2247 return (n + 1) * lineGap +
\r
2248 n * sizeInfo[boardSize].squareSize;
\r
2251 /* Respond to board resize by dragging edge */
\r
2253 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2255 BoardSize newSize = NUM_SIZES - 1;
\r
2256 static int recurse = 0;
\r
2257 if (IsIconic(hwndMain)) return;
\r
2258 if (recurse > 0) return;
\r
2260 while (newSize > 0) {
\r
2261 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2262 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2263 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2266 boardSize = newSize;
\r
2267 InitDrawingSizes(boardSize, flags);
\r
2272 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2275 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2277 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2278 ChessSquare piece;
\r
2279 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2281 SIZE clockSize, messageSize;
\r
2283 char buf[MSG_SIZ];
\r
2285 HMENU hmenu = GetMenu(hwndMain);
\r
2286 RECT crect, wrect, oldRect;
\r
2288 LOGBRUSH logbrush;
\r
2289 VariantClass v = gameInfo.variant;
\r
2291 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2292 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2294 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2295 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2296 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2297 oldBoardSize = boardSize;
\r
2299 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2300 { // correct board size to one where built-in pieces exist
\r
2301 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2302 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2304 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2305 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2306 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2307 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2308 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2309 boardSize = SizeMiddling;
\r
2312 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2314 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2315 oldRect.top = wpMain.y;
\r
2316 oldRect.right = wpMain.x + wpMain.width;
\r
2317 oldRect.bottom = wpMain.y + wpMain.height;
\r
2319 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2320 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2321 squareSize = sizeInfo[boardSize].squareSize;
\r
2322 lineGap = sizeInfo[boardSize].lineGap;
\r
2323 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2324 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2326 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2327 lineGap = appData.overrideLineGap;
\r
2330 if (tinyLayout != oldTinyLayout) {
\r
2331 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2333 style &= ~WS_SYSMENU;
\r
2334 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2335 "&Minimize\tCtrl+F4");
\r
2337 style |= WS_SYSMENU;
\r
2338 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2340 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2342 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2343 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2344 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2346 DrawMenuBar(hwndMain);
\r
2349 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2350 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2352 /* Get text area sizes */
\r
2353 hdc = GetDC(hwndMain);
\r
2354 if (appData.clockMode) {
\r
2355 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2357 snprintf(buf, MSG_SIZ, _("White"));
\r
2359 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2360 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2361 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2362 str = _("We only care about the height here");
\r
2363 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2364 SelectObject(hdc, oldFont);
\r
2365 ReleaseDC(hwndMain, hdc);
\r
2367 /* Compute where everything goes */
\r
2368 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2369 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2370 logoHeight = 2*clockSize.cy;
\r
2371 leftLogoRect.left = OUTER_MARGIN;
\r
2372 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2373 leftLogoRect.top = OUTER_MARGIN;
\r
2374 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2376 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2377 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2378 rightLogoRect.top = OUTER_MARGIN;
\r
2379 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2382 whiteRect.left = leftLogoRect.right;
\r
2383 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2384 whiteRect.top = OUTER_MARGIN;
\r
2385 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2387 blackRect.right = rightLogoRect.left;
\r
2388 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2389 blackRect.top = whiteRect.top;
\r
2390 blackRect.bottom = whiteRect.bottom;
\r
2392 whiteRect.left = OUTER_MARGIN;
\r
2393 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2394 whiteRect.top = OUTER_MARGIN;
\r
2395 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2397 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2398 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2399 blackRect.top = whiteRect.top;
\r
2400 blackRect.bottom = whiteRect.bottom;
\r
2402 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2405 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2406 if (appData.showButtonBar) {
\r
2407 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2408 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2410 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2412 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2413 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2415 boardRect.left = OUTER_MARGIN;
\r
2416 boardRect.right = boardRect.left + boardWidth;
\r
2417 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2418 boardRect.bottom = boardRect.top + boardHeight;
\r
2420 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2421 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2422 oldTinyLayout = tinyLayout;
\r
2423 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2424 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2425 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2426 winW *= 1 + twoBoards;
\r
2427 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2428 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2429 wpMain.height = winH; // without disturbing window attachments
\r
2430 GetWindowRect(hwndMain, &wrect);
\r
2431 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2432 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2434 // [HGM] placement: let attached windows follow size change.
\r
2435 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2436 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2437 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2438 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2439 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2441 /* compensate if menu bar wrapped */
\r
2442 GetClientRect(hwndMain, &crect);
\r
2443 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2444 wpMain.height += offby;
\r
2446 case WMSZ_TOPLEFT:
\r
2447 SetWindowPos(hwndMain, NULL,
\r
2448 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2449 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2452 case WMSZ_TOPRIGHT:
\r
2454 SetWindowPos(hwndMain, NULL,
\r
2455 wrect.left, wrect.bottom - wpMain.height,
\r
2456 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2459 case WMSZ_BOTTOMLEFT:
\r
2461 SetWindowPos(hwndMain, NULL,
\r
2462 wrect.right - wpMain.width, wrect.top,
\r
2463 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2466 case WMSZ_BOTTOMRIGHT:
\r
2470 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2471 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2476 for (i = 0; i < N_BUTTONS; i++) {
\r
2477 if (buttonDesc[i].hwnd != NULL) {
\r
2478 DestroyWindow(buttonDesc[i].hwnd);
\r
2479 buttonDesc[i].hwnd = NULL;
\r
2481 if (appData.showButtonBar) {
\r
2482 buttonDesc[i].hwnd =
\r
2483 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2484 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2485 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2486 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2487 (HMENU) buttonDesc[i].id,
\r
2488 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2490 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2491 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2492 MAKELPARAM(FALSE, 0));
\r
2494 if (buttonDesc[i].id == IDM_Pause)
\r
2495 hwndPause = buttonDesc[i].hwnd;
\r
2496 buttonDesc[i].wndproc = (WNDPROC)
\r
2497 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2500 if (gridPen != NULL) DeleteObject(gridPen);
\r
2501 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2502 if (premovePen != NULL) DeleteObject(premovePen);
\r
2503 if (lineGap != 0) {
\r
2504 logbrush.lbStyle = BS_SOLID;
\r
2505 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2507 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2508 lineGap, &logbrush, 0, NULL);
\r
2509 logbrush.lbColor = highlightSquareColor;
\r
2511 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2512 lineGap, &logbrush, 0, NULL);
\r
2514 logbrush.lbColor = premoveHighlightColor;
\r
2516 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2517 lineGap, &logbrush, 0, NULL);
\r
2519 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2520 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2521 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2522 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2523 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2524 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2525 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2526 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2528 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2529 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2530 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2531 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2532 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2533 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2534 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2535 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2539 /* [HGM] Licensing requirement */
\r
2541 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2544 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2546 GothicPopUp( "", VariantNormal);
\r
2549 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2551 /* Load piece bitmaps for this board size */
\r
2552 for (i=0; i<=2; i++) {
\r
2553 for (piece = WhitePawn;
\r
2554 (int) piece < (int) BlackPawn;
\r
2555 piece = (ChessSquare) ((int) piece + 1)) {
\r
2556 if (pieceBitmap[i][piece] != NULL)
\r
2557 DeleteObject(pieceBitmap[i][piece]);
\r
2561 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2562 // Orthodox Chess pieces
\r
2563 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2564 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2565 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2566 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2567 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2568 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2569 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2570 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2571 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2572 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2573 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2574 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2575 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2576 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2577 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2578 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2579 // in Shogi, Hijack the unused Queen for Lance
\r
2580 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2589 if(squareSize <= 72 && squareSize >= 33) {
\r
2590 /* A & C are available in most sizes now */
\r
2591 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2592 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", 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][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2601 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2602 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2603 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2604 } else { // Smirf-like
\r
2605 if(gameInfo.variant == VariantSChess) {
\r
2606 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2610 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2611 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2612 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2615 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2616 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2617 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2618 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2619 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2620 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2623 } else { // WinBoard standard
\r
2624 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2625 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2626 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2631 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2632 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2633 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2634 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2635 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2636 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2637 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2638 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2639 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2640 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2641 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2642 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2643 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2653 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2654 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2655 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2656 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2657 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2658 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2659 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2660 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2661 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2662 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2663 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2664 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2666 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2667 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2668 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2669 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2670 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2671 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2672 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2673 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2674 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2675 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2676 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2677 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2678 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2680 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2681 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2682 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2683 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2684 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2685 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2686 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2687 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2688 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2689 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2690 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2691 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2694 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2695 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2696 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2697 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2698 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2699 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2700 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2701 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2702 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2703 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2704 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2705 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2706 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2707 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2708 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2712 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2713 /* special Shogi support in this size */
\r
2714 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2715 for (piece = WhitePawn;
\r
2716 (int) piece < (int) BlackPawn;
\r
2717 piece = (ChessSquare) ((int) piece + 1)) {
\r
2718 if (pieceBitmap[i][piece] != NULL)
\r
2719 DeleteObject(pieceBitmap[i][piece]);
\r
2722 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2723 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2724 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2725 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2726 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2727 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2728 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2729 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2730 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2731 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2732 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2733 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2734 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2735 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2736 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2737 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2738 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2739 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2740 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2741 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2742 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2743 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2744 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2745 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2746 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2747 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2748 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2749 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2750 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2751 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2752 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2753 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2754 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2755 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2756 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2757 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2758 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2759 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2760 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2761 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2762 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2763 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2769 PieceBitmap(ChessSquare p, int kind)
\r
2771 if ((int) p >= (int) BlackPawn)
\r
2772 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2774 return pieceBitmap[kind][(int) p];
\r
2777 /***************************************************************/
\r
2779 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2780 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2782 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2783 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2787 SquareToPos(int row, int column, int * x, int * y)
\r
2790 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2791 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2793 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2794 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2799 DrawCoordsOnDC(HDC hdc)
\r
2801 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2802 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2803 char str[2] = { NULLCHAR, NULLCHAR };
\r
2804 int oldMode, oldAlign, x, y, start, i;
\r
2808 if (!appData.showCoords)
\r
2811 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2813 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2814 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2815 oldAlign = GetTextAlign(hdc);
\r
2816 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2818 y = boardRect.top + lineGap;
\r
2819 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2822 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2823 x += border - lineGap - 4; y += squareSize - 6;
\r
2825 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2826 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2827 str[0] = files[start + i];
\r
2828 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2829 y += squareSize + lineGap;
\r
2832 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2835 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2836 x += -border + 4; y += border - squareSize + 6;
\r
2838 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2839 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2840 str[0] = ranks[start + i];
\r
2841 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2842 x += squareSize + lineGap;
\r
2845 SelectObject(hdc, oldBrush);
\r
2846 SetBkMode(hdc, oldMode);
\r
2847 SetTextAlign(hdc, oldAlign);
\r
2848 SelectObject(hdc, oldFont);
\r
2852 DrawGridOnDC(HDC hdc)
\r
2856 if (lineGap != 0) {
\r
2857 oldPen = SelectObject(hdc, gridPen);
\r
2858 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2859 SelectObject(hdc, oldPen);
\r
2863 #define HIGHLIGHT_PEN 0
\r
2864 #define PREMOVE_PEN 1
\r
2867 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2870 HPEN oldPen, hPen;
\r
2871 if (lineGap == 0) return;
\r
2873 x1 = boardRect.left +
\r
2874 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2875 y1 = boardRect.top +
\r
2876 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2878 x1 = boardRect.left +
\r
2879 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2880 y1 = boardRect.top +
\r
2881 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2883 hPen = pen ? premovePen : highlightPen;
\r
2884 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2885 MoveToEx(hdc, x1, y1, NULL);
\r
2886 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2887 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2888 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2889 LineTo(hdc, x1, y1);
\r
2890 SelectObject(hdc, oldPen);
\r
2894 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2897 for (i=0; i<2; i++) {
\r
2898 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2899 DrawHighlightOnDC(hdc, TRUE,
\r
2900 h->sq[i].x, h->sq[i].y,
\r
2905 /* Note: sqcolor is used only in monoMode */
\r
2906 /* Note that this code is largely duplicated in woptions.c,
\r
2907 function DrawSampleSquare, so that needs to be updated too */
\r
2909 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2911 HBITMAP oldBitmap;
\r
2915 if (appData.blindfold) return;
\r
2917 /* [AS] Use font-based pieces if needed */
\r
2918 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2919 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2920 CreatePiecesFromFont();
\r
2922 if( fontBitmapSquareSize == squareSize ) {
\r
2923 int index = TranslatePieceToFontPiece(piece);
\r
2925 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2927 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2928 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2932 squareSize, squareSize,
\r
2937 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2939 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2940 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2944 squareSize, squareSize,
\r
2953 if (appData.monoMode) {
\r
2954 SelectObject(tmphdc, PieceBitmap(piece,
\r
2955 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2956 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2957 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2959 HBRUSH xBrush = whitePieceBrush;
\r
2960 tmpSize = squareSize;
\r
2961 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2963 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2964 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2965 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2966 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2967 x += (squareSize - minorSize)>>1;
\r
2968 y += squareSize - minorSize - 2;
\r
2969 tmpSize = minorSize;
\r
2971 if (color || appData.allWhite ) {
\r
2972 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2974 oldBrush = SelectObject(hdc, xBrush);
\r
2975 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2976 if(appData.upsideDown && color==flipView)
\r
2977 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2979 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2980 /* Use black for outline of white pieces */
\r
2981 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2982 if(appData.upsideDown && color==flipView)
\r
2983 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2985 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2986 } else if(appData.pieceDirectory[0]) {
\r
2987 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2988 oldBrush = SelectObject(hdc, xBrush);
\r
2989 if(appData.upsideDown && color==flipView)
\r
2990 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2992 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2993 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2994 if(appData.upsideDown && color==flipView)
\r
2995 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2997 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2999 /* Use square color for details of black pieces */
\r
3000 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3001 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3002 if(appData.upsideDown && !flipView)
\r
3003 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3005 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3007 SelectObject(hdc, oldBrush);
\r
3008 SelectObject(tmphdc, oldBitmap);
\r
3012 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3013 int GetBackTextureMode( int algo )
\r
3015 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3019 case BACK_TEXTURE_MODE_PLAIN:
\r
3020 result = 1; /* Always use identity map */
\r
3022 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3023 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3031 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3032 to handle redraws cleanly (as random numbers would always be different).
\r
3034 VOID RebuildTextureSquareInfo()
\r
3044 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3046 if( liteBackTexture != NULL ) {
\r
3047 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3048 lite_w = bi.bmWidth;
\r
3049 lite_h = bi.bmHeight;
\r
3053 if( darkBackTexture != NULL ) {
\r
3054 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3055 dark_w = bi.bmWidth;
\r
3056 dark_h = bi.bmHeight;
\r
3060 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3061 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3062 if( (col + row) & 1 ) {
\r
3064 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3065 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3066 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3068 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3069 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3070 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3072 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3073 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3078 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3079 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3080 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3082 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3083 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3084 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3086 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3087 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3094 /* [AS] Arrow highlighting support */
\r
3096 static double A_WIDTH = 5; /* Width of arrow body */
\r
3098 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3099 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3101 static double Sqr( double x )
\r
3106 static int Round( double x )
\r
3108 return (int) (x + 0.5);
\r
3111 /* Draw an arrow between two points using current settings */
\r
3112 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3115 double dx, dy, j, k, x, y;
\r
3117 if( d_x == s_x ) {
\r
3118 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3120 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3123 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3124 arrow[1].y = d_y - h;
\r
3126 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3127 arrow[2].y = d_y - h;
\r
3132 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3133 arrow[5].y = d_y - h;
\r
3135 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3136 arrow[4].y = d_y - h;
\r
3138 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3141 else if( d_y == s_y ) {
\r
3142 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3145 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3147 arrow[1].x = d_x - w;
\r
3148 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3150 arrow[2].x = d_x - w;
\r
3151 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3156 arrow[5].x = d_x - w;
\r
3157 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3159 arrow[4].x = d_x - w;
\r
3160 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3163 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3166 /* [AS] Needed a lot of paper for this! :-) */
\r
3167 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3168 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3170 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3172 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3177 arrow[0].x = Round(x - j);
\r
3178 arrow[0].y = Round(y + j*dx);
\r
3180 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3181 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3184 x = (double) d_x - k;
\r
3185 y = (double) d_y - k*dy;
\r
3188 x = (double) d_x + k;
\r
3189 y = (double) d_y + k*dy;
\r
3192 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3194 arrow[6].x = Round(x - j);
\r
3195 arrow[6].y = Round(y + j*dx);
\r
3197 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3198 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3200 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3201 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3206 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3207 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3210 Polygon( hdc, arrow, 7 );
\r
3213 /* [AS] Draw an arrow between two squares */
\r
3214 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3216 int s_x, s_y, d_x, d_y;
\r
3223 if( s_col == d_col && s_row == d_row ) {
\r
3227 /* Get source and destination points */
\r
3228 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3229 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3232 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3234 else if( d_y < s_y ) {
\r
3235 d_y += squareSize / 2 + squareSize / 4;
\r
3238 d_y += squareSize / 2;
\r
3242 d_x += squareSize / 2 - squareSize / 4;
\r
3244 else if( d_x < s_x ) {
\r
3245 d_x += squareSize / 2 + squareSize / 4;
\r
3248 d_x += squareSize / 2;
\r
3251 s_x += squareSize / 2;
\r
3252 s_y += squareSize / 2;
\r
3254 /* Adjust width */
\r
3255 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3258 stLB.lbStyle = BS_SOLID;
\r
3259 stLB.lbColor = appData.highlightArrowColor;
\r
3262 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3263 holdpen = SelectObject( hdc, hpen );
\r
3264 hbrush = CreateBrushIndirect( &stLB );
\r
3265 holdbrush = SelectObject( hdc, hbrush );
\r
3267 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3269 SelectObject( hdc, holdpen );
\r
3270 SelectObject( hdc, holdbrush );
\r
3271 DeleteObject( hpen );
\r
3272 DeleteObject( hbrush );
\r
3275 BOOL HasHighlightInfo()
\r
3277 BOOL result = FALSE;
\r
3279 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3280 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3291 BOOL IsDrawArrowEnabled()
\r
3293 BOOL result = FALSE;
\r
3295 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3302 VOID DrawArrowHighlight( HDC hdc )
\r
3304 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3305 DrawArrowBetweenSquares( hdc,
\r
3306 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3307 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3311 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3313 HRGN result = NULL;
\r
3315 if( HasHighlightInfo() ) {
\r
3316 int x1, y1, x2, y2;
\r
3317 int sx, sy, dx, dy;
\r
3319 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3320 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3322 sx = MIN( x1, x2 );
\r
3323 sy = MIN( y1, y2 );
\r
3324 dx = MAX( x1, x2 ) + squareSize;
\r
3325 dy = MAX( y1, y2 ) + squareSize;
\r
3327 result = CreateRectRgn( sx, sy, dx, dy );
\r
3334 Warning: this function modifies the behavior of several other functions.
\r
3336 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3337 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3338 repaint is scattered all over the place, which is not good for features such as
\r
3339 "arrow highlighting" that require a full repaint of the board.
\r
3341 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3342 user interaction, when speed is not so important) but especially to avoid errors
\r
3343 in the displayed graphics.
\r
3345 In such patched places, I always try refer to this function so there is a single
\r
3346 place to maintain knowledge.
\r
3348 To restore the original behavior, just return FALSE unconditionally.
\r
3350 BOOL IsFullRepaintPreferrable()
\r
3352 BOOL result = FALSE;
\r
3354 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3355 /* Arrow may appear on the board */
\r
3363 This function is called by DrawPosition to know whether a full repaint must
\r
3366 Only DrawPosition may directly call this function, which makes use of
\r
3367 some state information. Other function should call DrawPosition specifying
\r
3368 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3370 BOOL DrawPositionNeedsFullRepaint()
\r
3372 BOOL result = FALSE;
\r
3375 Probably a slightly better policy would be to trigger a full repaint
\r
3376 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3377 but animation is fast enough that it's difficult to notice.
\r
3379 if( animInfo.piece == EmptySquare ) {
\r
3380 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3388 static HBITMAP borderBitmap;
\r
3391 DrawBackgroundOnDC(HDC hdc)
\r
3397 static char oldBorder[MSG_SIZ];
\r
3398 int w = 600, h = 600, mode;
\r
3400 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3401 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3402 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3404 if(borderBitmap == NULL) { // loading failed, use white
\r
3405 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3408 tmphdc = CreateCompatibleDC(hdc);
\r
3409 hbm = SelectObject(tmphdc, borderBitmap);
\r
3410 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3414 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3415 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3416 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3417 SetStretchBltMode(hdc, mode);
\r
3418 SelectObject(tmphdc, hbm);
\r
3423 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3425 int row, column, x, y, square_color, piece_color;
\r
3426 ChessSquare piece;
\r
3428 HDC texture_hdc = NULL;
\r
3430 /* [AS] Initialize background textures if needed */
\r
3431 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3432 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3433 if( backTextureSquareSize != squareSize
\r
3434 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3435 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3436 backTextureSquareSize = squareSize;
\r
3437 RebuildTextureSquareInfo();
\r
3440 texture_hdc = CreateCompatibleDC( hdc );
\r
3443 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3444 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3446 SquareToPos(row, column, &x, &y);
\r
3448 piece = board[row][column];
\r
3450 square_color = ((column + row) % 2) == 1;
\r
3451 if( gameInfo.variant == VariantXiangqi ) {
\r
3452 square_color = !InPalace(row, column);
\r
3453 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3454 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3456 piece_color = (int) piece < (int) BlackPawn;
\r
3459 /* [HGM] holdings file: light square or black */
\r
3460 if(column == BOARD_LEFT-2) {
\r
3461 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3464 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3468 if(column == BOARD_RGHT + 1 ) {
\r
3469 if( row < gameInfo.holdingsSize )
\r
3472 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3476 if(column == BOARD_LEFT-1 ) /* left align */
\r
3477 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3478 else if( column == BOARD_RGHT) /* right align */
\r
3479 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3480 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3482 if (appData.monoMode) {
\r
3483 if (piece == EmptySquare) {
\r
3484 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3485 square_color ? WHITENESS : BLACKNESS);
\r
3487 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3490 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3491 /* [AS] Draw the square using a texture bitmap */
\r
3492 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3493 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3494 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3497 squareSize, squareSize,
\r
3500 backTextureSquareInfo[r][c].mode,
\r
3501 backTextureSquareInfo[r][c].x,
\r
3502 backTextureSquareInfo[r][c].y );
\r
3504 SelectObject( texture_hdc, hbm );
\r
3506 if (piece != EmptySquare) {
\r
3507 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3511 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3513 oldBrush = SelectObject(hdc, brush );
\r
3514 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3515 SelectObject(hdc, oldBrush);
\r
3516 if (piece != EmptySquare)
\r
3517 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3522 if( texture_hdc != NULL ) {
\r
3523 DeleteDC( texture_hdc );
\r
3527 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3528 void fputDW(FILE *f, int x)
\r
3530 fputc(x & 255, f);
\r
3531 fputc(x>>8 & 255, f);
\r
3532 fputc(x>>16 & 255, f);
\r
3533 fputc(x>>24 & 255, f);
\r
3536 #define MAX_CLIPS 200 /* more than enough */
\r
3539 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3541 // HBITMAP bufferBitmap;
\r
3546 int w = 100, h = 50;
\r
3548 if(logo == NULL) {
\r
3549 if(!logoHeight) return;
\r
3550 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3552 // GetClientRect(hwndMain, &Rect);
\r
3553 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3554 // Rect.bottom-Rect.top+1);
\r
3555 tmphdc = CreateCompatibleDC(hdc);
\r
3556 hbm = SelectObject(tmphdc, logo);
\r
3557 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3561 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3562 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3563 SelectObject(tmphdc, hbm);
\r
3571 HDC hdc = GetDC(hwndMain);
\r
3572 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3573 if(appData.autoLogo) {
\r
3575 switch(gameMode) { // pick logos based on game mode
\r
3576 case IcsObserving:
\r
3577 whiteLogo = second.programLogo; // ICS logo
\r
3578 blackLogo = second.programLogo;
\r
3581 case IcsPlayingWhite:
\r
3582 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3583 blackLogo = second.programLogo; // ICS logo
\r
3585 case IcsPlayingBlack:
\r
3586 whiteLogo = second.programLogo; // ICS logo
\r
3587 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3589 case TwoMachinesPlay:
\r
3590 if(first.twoMachinesColor[0] == 'b') {
\r
3591 whiteLogo = second.programLogo;
\r
3592 blackLogo = first.programLogo;
\r
3595 case MachinePlaysWhite:
\r
3596 blackLogo = userLogo;
\r
3598 case MachinePlaysBlack:
\r
3599 whiteLogo = userLogo;
\r
3600 blackLogo = first.programLogo;
\r
3603 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3604 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3605 ReleaseDC(hwndMain, hdc);
\r
3610 UpdateLogos(int display)
\r
3611 { // called after loading new engine(s), in tourney or from menu
\r
3612 LoadLogo(&first, 0, FALSE);
\r
3613 LoadLogo(&second, 1, appData.icsActive);
\r
3614 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3615 if(display) DisplayLogos();
\r
3618 static HDC hdcSeek;
\r
3620 // [HGM] seekgraph
\r
3621 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3624 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3625 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3626 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3627 SelectObject( hdcSeek, hp );
\r
3630 // front-end wrapper for drawing functions to do rectangles
\r
3631 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3636 if (hdcSeek == NULL) {
\r
3637 hdcSeek = GetDC(hwndMain);
\r
3638 if (!appData.monoMode) {
\r
3639 SelectPalette(hdcSeek, hPal, FALSE);
\r
3640 RealizePalette(hdcSeek);
\r
3643 hp = SelectObject( hdcSeek, gridPen );
\r
3644 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3645 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3646 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3647 SelectObject( hdcSeek, hp );
\r
3650 // front-end wrapper for putting text in graph
\r
3651 void DrawSeekText(char *buf, int x, int y)
\r
3654 SetBkMode( hdcSeek, TRANSPARENT );
\r
3655 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3656 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3659 void DrawSeekDot(int x, int y, int color)
\r
3661 int square = color & 0x80;
\r
3662 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3663 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3666 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3667 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3669 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3670 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3671 SelectObject(hdcSeek, oldBrush);
\r
3674 void DrawSeekOpen()
\r
3678 void DrawSeekClose()
\r
3683 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3685 static Board lastReq[2], lastDrawn[2];
\r
3686 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3687 static int lastDrawnFlipView = 0;
\r
3688 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3689 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3692 HBITMAP bufferBitmap;
\r
3693 HBITMAP oldBitmap;
\r
3695 HRGN clips[MAX_CLIPS];
\r
3696 ChessSquare dragged_piece = EmptySquare;
\r
3697 int nr = twoBoards*partnerUp;
\r
3699 /* I'm undecided on this - this function figures out whether a full
\r
3700 * repaint is necessary on its own, so there's no real reason to have the
\r
3701 * caller tell it that. I think this can safely be set to FALSE - but
\r
3702 * if we trust the callers not to request full repaints unnessesarily, then
\r
3703 * we could skip some clipping work. In other words, only request a full
\r
3704 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3705 * gamestart and similar) --Hawk
\r
3707 Boolean fullrepaint = repaint;
\r
3709 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3711 if( DrawPositionNeedsFullRepaint() ) {
\r
3712 fullrepaint = TRUE;
\r
3715 if (board == NULL) {
\r
3716 if (!lastReqValid[nr]) {
\r
3719 board = lastReq[nr];
\r
3721 CopyBoard(lastReq[nr], board);
\r
3722 lastReqValid[nr] = 1;
\r
3725 if (doingSizing) {
\r
3729 if (IsIconic(hwndMain)) {
\r
3733 if (hdc == NULL) {
\r
3734 hdc = GetDC(hwndMain);
\r
3735 if (!appData.monoMode) {
\r
3736 SelectPalette(hdc, hPal, FALSE);
\r
3737 RealizePalette(hdc);
\r
3741 releaseDC = FALSE;
\r
3744 /* Create some work-DCs */
\r
3745 hdcmem = CreateCompatibleDC(hdc);
\r
3746 tmphdc = CreateCompatibleDC(hdc);
\r
3748 /* If dragging is in progress, we temporarely remove the piece */
\r
3749 /* [HGM] or temporarily decrease count if stacked */
\r
3750 /* !! Moved to before board compare !! */
\r
3751 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3752 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3753 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3754 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3755 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3757 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3758 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3759 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3761 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3764 /* Figure out which squares need updating by comparing the
\r
3765 * newest board with the last drawn board and checking if
\r
3766 * flipping has changed.
\r
3768 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3769 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3770 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3771 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3772 SquareToPos(row, column, &x, &y);
\r
3773 clips[num_clips++] =
\r
3774 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3778 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3779 for (i=0; i<2; i++) {
\r
3780 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3781 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3782 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3783 lastDrawnHighlight.sq[i].y >= 0) {
\r
3784 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3785 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3786 clips[num_clips++] =
\r
3787 CreateRectRgn(x - lineGap, y - lineGap,
\r
3788 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3790 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3791 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3792 clips[num_clips++] =
\r
3793 CreateRectRgn(x - lineGap, y - lineGap,
\r
3794 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3798 for (i=0; i<2; i++) {
\r
3799 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3800 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3801 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3802 lastDrawnPremove.sq[i].y >= 0) {
\r
3803 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3804 lastDrawnPremove.sq[i].x, &x, &y);
\r
3805 clips[num_clips++] =
\r
3806 CreateRectRgn(x - lineGap, y - lineGap,
\r
3807 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3809 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3810 premoveHighlightInfo.sq[i].y >= 0) {
\r
3811 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3812 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3813 clips[num_clips++] =
\r
3814 CreateRectRgn(x - lineGap, y - lineGap,
\r
3815 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3819 } else { // nr == 1
\r
3820 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3821 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3822 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3823 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3824 for (i=0; i<2; i++) {
\r
3825 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3826 partnerHighlightInfo.sq[i].y >= 0) {
\r
3827 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3828 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3829 clips[num_clips++] =
\r
3830 CreateRectRgn(x - lineGap, y - lineGap,
\r
3831 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3833 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3834 oldPartnerHighlight.sq[i].y >= 0) {
\r
3835 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3836 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3837 clips[num_clips++] =
\r
3838 CreateRectRgn(x - lineGap, y - lineGap,
\r
3839 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3844 fullrepaint = TRUE;
\r
3847 /* Create a buffer bitmap - this is the actual bitmap
\r
3848 * being written to. When all the work is done, we can
\r
3849 * copy it to the real DC (the screen). This avoids
\r
3850 * the problems with flickering.
\r
3852 GetClientRect(hwndMain, &Rect);
\r
3853 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3854 Rect.bottom-Rect.top+1);
\r
3855 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3856 if (!appData.monoMode) {
\r
3857 SelectPalette(hdcmem, hPal, FALSE);
\r
3860 /* Create clips for dragging */
\r
3861 if (!fullrepaint) {
\r
3862 if (dragInfo.from.x >= 0) {
\r
3863 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3864 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3866 if (dragInfo.start.x >= 0) {
\r
3867 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3868 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3870 if (dragInfo.pos.x >= 0) {
\r
3871 x = dragInfo.pos.x - squareSize / 2;
\r
3872 y = dragInfo.pos.y - squareSize / 2;
\r
3873 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3875 if (dragInfo.lastpos.x >= 0) {
\r
3876 x = dragInfo.lastpos.x - squareSize / 2;
\r
3877 y = dragInfo.lastpos.y - squareSize / 2;
\r
3878 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3882 /* Are we animating a move?
\r
3884 * - remove the piece from the board (temporarely)
\r
3885 * - calculate the clipping region
\r
3887 if (!fullrepaint) {
\r
3888 if (animInfo.piece != EmptySquare) {
\r
3889 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3890 x = boardRect.left + animInfo.lastpos.x;
\r
3891 y = boardRect.top + animInfo.lastpos.y;
\r
3892 x2 = boardRect.left + animInfo.pos.x;
\r
3893 y2 = boardRect.top + animInfo.pos.y;
\r
3894 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3895 /* Slight kludge. The real problem is that after AnimateMove is
\r
3896 done, the position on the screen does not match lastDrawn.
\r
3897 This currently causes trouble only on e.p. captures in
\r
3898 atomic, where the piece moves to an empty square and then
\r
3899 explodes. The old and new positions both had an empty square
\r
3900 at the destination, but animation has drawn a piece there and
\r
3901 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3902 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3906 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3907 if (num_clips == 0)
\r
3908 fullrepaint = TRUE;
\r
3910 /* Set clipping on the memory DC */
\r
3911 if (!fullrepaint) {
\r
3912 SelectClipRgn(hdcmem, clips[0]);
\r
3913 for (x = 1; x < num_clips; x++) {
\r
3914 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3915 abort(); // this should never ever happen!
\r
3919 /* Do all the drawing to the memory DC */
\r
3920 if(explodeInfo.radius) { // [HGM] atomic
\r
3922 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3923 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3924 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3925 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3926 x += squareSize/2;
\r
3927 y += squareSize/2;
\r
3928 if(!fullrepaint) {
\r
3929 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3930 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3932 DrawGridOnDC(hdcmem);
\r
3933 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3934 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3935 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3936 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3937 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3938 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3939 SelectObject(hdcmem, oldBrush);
\r
3941 if(border) DrawBackgroundOnDC(hdcmem);
\r
3942 DrawGridOnDC(hdcmem);
\r
3943 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3944 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3945 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3947 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3948 oldPartnerHighlight = partnerHighlightInfo;
\r
3950 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3952 if(nr == 0) // [HGM] dual: markers only on left board
\r
3953 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3954 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3955 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3956 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3957 SquareToPos(row, column, &x, &y);
\r
3958 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3959 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3960 SelectObject(hdcmem, oldBrush);
\r
3965 if( appData.highlightMoveWithArrow ) {
\r
3966 DrawArrowHighlight(hdcmem);
\r
3969 DrawCoordsOnDC(hdcmem);
\r
3971 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3972 /* to make sure lastDrawn contains what is actually drawn */
\r
3974 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3975 if (dragged_piece != EmptySquare) {
\r
3976 /* [HGM] or restack */
\r
3977 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3978 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3980 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3981 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3983 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3984 x = dragInfo.pos.x - squareSize / 2;
\r
3985 y = dragInfo.pos.y - squareSize / 2;
\r
3986 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3987 ((int) dragInfo.piece < (int) BlackPawn),
\r
3988 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3991 /* Put the animated piece back into place and draw it */
\r
3992 if (animInfo.piece != EmptySquare) {
\r
3993 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3994 x = boardRect.left + animInfo.pos.x;
\r
3995 y = boardRect.top + animInfo.pos.y;
\r
3996 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3997 ((int) animInfo.piece < (int) BlackPawn),
\r
3998 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4001 /* Release the bufferBitmap by selecting in the old bitmap
\r
4002 * and delete the memory DC
\r
4004 SelectObject(hdcmem, oldBitmap);
\r
4007 /* Set clipping on the target DC */
\r
4008 if (!fullrepaint) {
\r
4009 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4011 GetRgnBox(clips[x], &rect);
\r
4012 DeleteObject(clips[x]);
\r
4013 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4014 rect.right + wpMain.width/2, rect.bottom);
\r
4016 SelectClipRgn(hdc, clips[0]);
\r
4017 for (x = 1; x < num_clips; x++) {
\r
4018 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4019 abort(); // this should never ever happen!
\r
4023 /* Copy the new bitmap onto the screen in one go.
\r
4024 * This way we avoid any flickering
\r
4026 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4027 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4028 boardRect.right - boardRect.left,
\r
4029 boardRect.bottom - boardRect.top,
\r
4030 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4031 if(saveDiagFlag) {
\r
4032 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4033 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4035 GetObject(bufferBitmap, sizeof(b), &b);
\r
4036 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4037 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4038 bih.biWidth = b.bmWidth;
\r
4039 bih.biHeight = b.bmHeight;
\r
4041 bih.biBitCount = b.bmBitsPixel;
\r
4042 bih.biCompression = 0;
\r
4043 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4044 bih.biXPelsPerMeter = 0;
\r
4045 bih.biYPelsPerMeter = 0;
\r
4046 bih.biClrUsed = 0;
\r
4047 bih.biClrImportant = 0;
\r
4048 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4049 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4050 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4051 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4053 wb = b.bmWidthBytes;
\r
4055 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4056 int k = ((int*) pData)[i];
\r
4057 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4058 if(j >= 16) break;
\r
4060 if(j >= nrColors) nrColors = j+1;
\r
4062 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4064 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4065 for(w=0; w<(wb>>2); w+=2) {
\r
4066 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4067 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4068 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4069 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4070 pData[p++] = m | j<<4;
\r
4072 while(p&3) pData[p++] = 0;
\r
4075 wb = ((wb+31)>>5)<<2;
\r
4077 // write BITMAPFILEHEADER
\r
4078 fprintf(diagFile, "BM");
\r
4079 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4080 fputDW(diagFile, 0);
\r
4081 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4082 // write BITMAPINFOHEADER
\r
4083 fputDW(diagFile, 40);
\r
4084 fputDW(diagFile, b.bmWidth);
\r
4085 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4086 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4087 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4088 fputDW(diagFile, 0);
\r
4089 fputDW(diagFile, 0);
\r
4090 fputDW(diagFile, 0);
\r
4091 fputDW(diagFile, 0);
\r
4092 fputDW(diagFile, 0);
\r
4093 fputDW(diagFile, 0);
\r
4094 // write color table
\r
4096 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4097 // write bitmap data
\r
4098 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4099 fputc(pData[i], diagFile);
\r
4104 SelectObject(tmphdc, oldBitmap);
\r
4106 /* Massive cleanup */
\r
4107 for (x = 0; x < num_clips; x++)
\r
4108 DeleteObject(clips[x]);
\r
4111 DeleteObject(bufferBitmap);
\r
4114 ReleaseDC(hwndMain, hdc);
\r
4116 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4118 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4120 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4123 /* CopyBoard(lastDrawn, board);*/
\r
4124 lastDrawnHighlight = highlightInfo;
\r
4125 lastDrawnPremove = premoveHighlightInfo;
\r
4126 lastDrawnFlipView = flipView;
\r
4127 lastDrawnValid[nr] = 1;
\r
4130 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4135 saveDiagFlag = 1; diagFile = f;
\r
4136 HDCDrawPosition(NULL, TRUE, NULL);
\r
4144 /*---------------------------------------------------------------------------*\
\r
4145 | CLIENT PAINT PROCEDURE
\r
4146 | This is the main event-handler for the WM_PAINT message.
\r
4148 \*---------------------------------------------------------------------------*/
\r
4150 PaintProc(HWND hwnd)
\r
4156 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4157 if (IsIconic(hwnd)) {
\r
4158 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4160 if (!appData.monoMode) {
\r
4161 SelectPalette(hdc, hPal, FALSE);
\r
4162 RealizePalette(hdc);
\r
4164 HDCDrawPosition(hdc, 1, NULL);
\r
4165 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4166 flipView = !flipView; partnerUp = !partnerUp;
\r
4167 HDCDrawPosition(hdc, 1, NULL);
\r
4168 flipView = !flipView; partnerUp = !partnerUp;
\r
4171 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4172 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4173 ETO_CLIPPED|ETO_OPAQUE,
\r
4174 &messageRect, messageText, strlen(messageText), NULL);
\r
4175 SelectObject(hdc, oldFont);
\r
4176 DisplayBothClocks();
\r
4179 EndPaint(hwnd,&ps);
\r
4187 * If the user selects on a border boundary, return -1; if off the board,
\r
4188 * return -2. Otherwise map the event coordinate to the square.
\r
4189 * The offset boardRect.left or boardRect.top must already have been
\r
4190 * subtracted from x.
\r
4192 int EventToSquare(x, limit)
\r
4197 if (x < lineGap + border)
\r
4199 x -= lineGap + border;
\r
4200 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4202 x /= (squareSize + lineGap);
\r
4214 DropEnable dropEnables[] = {
\r
4215 { 'P', DP_Pawn, N_("Pawn") },
\r
4216 { 'N', DP_Knight, N_("Knight") },
\r
4217 { 'B', DP_Bishop, N_("Bishop") },
\r
4218 { 'R', DP_Rook, N_("Rook") },
\r
4219 { 'Q', DP_Queen, N_("Queen") },
\r
4223 SetupDropMenu(HMENU hmenu)
\r
4225 int i, count, enable;
\r
4227 extern char white_holding[], black_holding[];
\r
4228 char item[MSG_SIZ];
\r
4230 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4231 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4232 dropEnables[i].piece);
\r
4234 while (p && *p++ == dropEnables[i].piece) count++;
\r
4235 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4236 enable = count > 0 || !appData.testLegality
\r
4237 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4238 && !appData.icsActive);
\r
4239 ModifyMenu(hmenu, dropEnables[i].command,
\r
4240 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4241 dropEnables[i].command, item);
\r
4245 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4247 dragInfo.lastpos.x = boardRect.left + x;
\r
4248 dragInfo.lastpos.y = boardRect.top + y;
\r
4249 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4250 dragInfo.from.x = fromX;
\r
4251 dragInfo.from.y = fromY;
\r
4252 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4253 dragInfo.start = dragInfo.from;
\r
4254 SetCapture(hwndMain);
\r
4257 void DragPieceEnd(int x, int y)
\r
4260 dragInfo.start.x = dragInfo.start.y = -1;
\r
4261 dragInfo.from = dragInfo.start;
\r
4262 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4265 void ChangeDragPiece(ChessSquare piece)
\r
4267 dragInfo.piece = piece;
\r
4270 /* Event handler for mouse messages */
\r
4272 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4276 static int recursive = 0;
\r
4278 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4281 if (message == WM_MBUTTONUP) {
\r
4282 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4283 to the middle button: we simulate pressing the left button too!
\r
4285 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4286 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4292 pt.x = LOWORD(lParam);
\r
4293 pt.y = HIWORD(lParam);
\r
4294 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4295 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4296 if (!flipView && y >= 0) {
\r
4297 y = BOARD_HEIGHT - 1 - y;
\r
4299 if (flipView && x >= 0) {
\r
4300 x = BOARD_WIDTH - 1 - x;
\r
4303 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4304 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4306 switch (message) {
\r
4307 case WM_LBUTTONDOWN:
\r
4308 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4309 ClockClick(flipClock); break;
\r
4310 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4311 ClockClick(!flipClock); break;
\r
4313 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4314 dragInfo.start.x = dragInfo.start.y = -1;
\r
4315 dragInfo.from = dragInfo.start;
\r
4317 if(fromX == -1 && frozen) { // not sure where this is for
\r
4318 fromX = fromY = -1;
\r
4319 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4322 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4323 DrawPosition(TRUE, NULL);
\r
4326 case WM_LBUTTONUP:
\r
4327 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4328 DrawPosition(TRUE, NULL);
\r
4331 case WM_MOUSEMOVE:
\r
4332 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4333 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4334 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4335 if ((appData.animateDragging || appData.highlightDragging)
\r
4336 && (wParam & MK_LBUTTON || dragging == 2)
\r
4337 && dragInfo.from.x >= 0)
\r
4339 BOOL full_repaint = FALSE;
\r
4341 if (appData.animateDragging) {
\r
4342 dragInfo.pos = pt;
\r
4344 if (appData.highlightDragging) {
\r
4345 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4346 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4347 full_repaint = TRUE;
\r
4351 DrawPosition( full_repaint, NULL);
\r
4353 dragInfo.lastpos = dragInfo.pos;
\r
4357 case WM_MOUSEWHEEL: // [DM]
\r
4358 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4359 /* Mouse Wheel is being rolled forward
\r
4360 * Play moves forward
\r
4362 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4363 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4364 /* Mouse Wheel is being rolled backward
\r
4365 * Play moves backward
\r
4367 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4368 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4372 case WM_MBUTTONUP:
\r
4373 case WM_RBUTTONUP:
\r
4375 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4378 case WM_MBUTTONDOWN:
\r
4379 case WM_RBUTTONDOWN:
\r
4382 fromX = fromY = -1;
\r
4383 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4384 dragInfo.start.x = dragInfo.start.y = -1;
\r
4385 dragInfo.from = dragInfo.start;
\r
4386 dragInfo.lastpos = dragInfo.pos;
\r
4387 if (appData.highlightDragging) {
\r
4388 ClearHighlights();
\r
4391 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4392 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4393 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4394 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4395 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4399 DrawPosition(TRUE, NULL);
\r
4401 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4404 if (message == WM_MBUTTONDOWN) {
\r
4405 buttonCount = 3; /* even if system didn't think so */
\r
4406 if (wParam & MK_SHIFT)
\r
4407 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4409 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4410 } else { /* message == WM_RBUTTONDOWN */
\r
4411 /* Just have one menu, on the right button. Windows users don't
\r
4412 think to try the middle one, and sometimes other software steals
\r
4413 it, or it doesn't really exist. */
\r
4414 if(gameInfo.variant != VariantShogi)
\r
4415 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4417 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4421 SetCapture(hwndMain);
\r
4424 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4425 SetupDropMenu(hmenu);
\r
4426 MenuPopup(hwnd, pt, hmenu, -1);
\r
4436 /* Preprocess messages for buttons in main window */
\r
4438 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4440 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4443 for (i=0; i<N_BUTTONS; i++) {
\r
4444 if (buttonDesc[i].id == id) break;
\r
4446 if (i == N_BUTTONS) return 0;
\r
4447 switch (message) {
\r
4452 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4453 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4460 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4463 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4464 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4465 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4466 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4468 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4470 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4471 TypeInEvent((char)wParam);
\r
4477 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4480 static int promoStyle;
\r
4482 /* Process messages for Promotion dialog box */
\r
4484 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4488 switch (message) {
\r
4489 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4490 /* Center the dialog over the application window */
\r
4491 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4492 Translate(hDlg, DLG_PromotionKing);
\r
4493 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4494 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4495 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4496 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4497 SW_SHOW : SW_HIDE);
\r
4498 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4499 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4500 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4501 PieceToChar(WhiteAngel) != '~') ||
\r
4502 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4503 PieceToChar(BlackAngel) != '~') ) ?
\r
4504 SW_SHOW : SW_HIDE);
\r
4505 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4506 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4507 PieceToChar(WhiteMarshall) != '~') ||
\r
4508 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4509 PieceToChar(BlackMarshall) != '~') ) ?
\r
4510 SW_SHOW : SW_HIDE);
\r
4511 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4512 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4513 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4515 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4516 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4517 SetWindowText(hDlg, "Promote?");
\r
4519 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4520 gameInfo.variant == VariantSuper ?
\r
4521 SW_SHOW : SW_HIDE);
\r
4524 case WM_COMMAND: /* message: received a command */
\r
4525 switch (LOWORD(wParam)) {
\r
4527 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4528 ClearHighlights();
\r
4529 DrawPosition(FALSE, NULL);
\r
4532 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4535 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4538 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4539 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4542 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4543 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4545 case PB_Chancellor:
\r
4546 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4548 case PB_Archbishop:
\r
4549 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4552 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4553 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4558 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4559 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4560 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4561 fromX = fromY = -1;
\r
4562 if (!appData.highlightLastMove) {
\r
4563 ClearHighlights();
\r
4564 DrawPosition(FALSE, NULL);
\r
4571 /* Pop up promotion dialog */
\r
4573 PromotionPopup(HWND hwnd)
\r
4577 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4578 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4579 hwnd, (DLGPROC)lpProc);
\r
4580 FreeProcInstance(lpProc);
\r
4584 PromotionPopUp(char choice)
\r
4586 promoStyle = (choice == '+');
\r
4587 DrawPosition(TRUE, NULL);
\r
4588 PromotionPopup(hwndMain);
\r
4592 LoadGameDialog(HWND hwnd, char* title)
\r
4596 char fileTitle[MSG_SIZ];
\r
4597 f = OpenFileDialog(hwnd, "rb", "",
\r
4598 appData.oldSaveStyle ? "gam" : "pgn",
\r
4600 title, &number, fileTitle, NULL);
\r
4602 cmailMsgLoaded = FALSE;
\r
4603 if (number == 0) {
\r
4604 int error = GameListBuild(f);
\r
4606 DisplayError(_("Cannot build game list"), error);
\r
4607 } else if (!ListEmpty(&gameList) &&
\r
4608 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4609 GameListPopUp(f, fileTitle);
\r
4612 GameListDestroy();
\r
4615 LoadGame(f, number, fileTitle, FALSE);
\r
4619 int get_term_width()
\r
4624 HFONT hfont, hold_font;
\r
4629 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4633 // get the text metrics
\r
4634 hdc = GetDC(hText);
\r
4635 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4636 if (consoleCF.dwEffects & CFE_BOLD)
\r
4637 lf.lfWeight = FW_BOLD;
\r
4638 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4639 lf.lfItalic = TRUE;
\r
4640 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4641 lf.lfStrikeOut = TRUE;
\r
4642 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4643 lf.lfUnderline = TRUE;
\r
4644 hfont = CreateFontIndirect(&lf);
\r
4645 hold_font = SelectObject(hdc, hfont);
\r
4646 GetTextMetrics(hdc, &tm);
\r
4647 SelectObject(hdc, hold_font);
\r
4648 DeleteObject(hfont);
\r
4649 ReleaseDC(hText, hdc);
\r
4651 // get the rectangle
\r
4652 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4654 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4657 void UpdateICSWidth(HWND hText)
\r
4659 LONG old_width, new_width;
\r
4661 new_width = get_term_width(hText, FALSE);
\r
4662 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4663 if (new_width != old_width)
\r
4665 ics_update_width(new_width);
\r
4666 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4671 ChangedConsoleFont()
\r
4674 CHARRANGE tmpsel, sel;
\r
4675 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4676 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4677 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4680 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4681 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4682 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4683 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4684 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4685 * size. This was undocumented in the version of MSVC++ that I had
\r
4686 * when I wrote the code, but is apparently documented now.
\r
4688 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4689 cfmt.bCharSet = f->lf.lfCharSet;
\r
4690 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4691 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4692 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4693 /* Why are the following seemingly needed too? */
\r
4694 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4695 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4696 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4698 tmpsel.cpMax = -1; /*999999?*/
\r
4699 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4700 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4701 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4702 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4704 paraf.cbSize = sizeof(paraf);
\r
4705 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4706 paraf.dxStartIndent = 0;
\r
4707 paraf.dxOffset = WRAP_INDENT;
\r
4708 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4709 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4710 UpdateICSWidth(hText);
\r
4713 /*---------------------------------------------------------------------------*\
\r
4715 * Window Proc for main window
\r
4717 \*---------------------------------------------------------------------------*/
\r
4719 /* Process messages for main window, etc. */
\r
4721 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4728 char fileTitle[MSG_SIZ];
\r
4729 static SnapData sd;
\r
4730 static int peek=0;
\r
4732 switch (message) {
\r
4734 case WM_PAINT: /* message: repaint portion of window */
\r
4738 case WM_ERASEBKGND:
\r
4739 if (IsIconic(hwnd)) {
\r
4740 /* Cheat; change the message */
\r
4741 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4743 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4747 case WM_LBUTTONDOWN:
\r
4748 case WM_MBUTTONDOWN:
\r
4749 case WM_RBUTTONDOWN:
\r
4750 case WM_LBUTTONUP:
\r
4751 case WM_MBUTTONUP:
\r
4752 case WM_RBUTTONUP:
\r
4753 case WM_MOUSEMOVE:
\r
4754 case WM_MOUSEWHEEL:
\r
4755 MouseEvent(hwnd, message, wParam, lParam);
\r
4759 if((char)wParam == '\b') {
\r
4760 ForwardEvent(); peek = 0;
\r
4763 JAWS_KBUP_NAVIGATION
\r
4768 if((char)wParam == '\b') {
\r
4769 if(!peek) BackwardEvent(), peek = 1;
\r
4772 JAWS_KBDOWN_NAVIGATION
\r
4778 JAWS_ALT_INTERCEPT
\r
4780 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4781 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4782 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4783 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4785 SendMessage(h, message, wParam, lParam);
\r
4786 } else if(lParam != KF_REPEAT) {
\r
4787 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4788 TypeInEvent((char)wParam);
\r
4789 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4790 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4795 case WM_PALETTECHANGED:
\r
4796 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4798 HDC hdc = GetDC(hwndMain);
\r
4799 SelectPalette(hdc, hPal, TRUE);
\r
4800 nnew = RealizePalette(hdc);
\r
4802 paletteChanged = TRUE;
\r
4804 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4806 ReleaseDC(hwnd, hdc);
\r
4810 case WM_QUERYNEWPALETTE:
\r
4811 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4813 HDC hdc = GetDC(hwndMain);
\r
4814 paletteChanged = FALSE;
\r
4815 SelectPalette(hdc, hPal, FALSE);
\r
4816 nnew = RealizePalette(hdc);
\r
4818 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4820 ReleaseDC(hwnd, hdc);
\r
4825 case WM_COMMAND: /* message: command from application menu */
\r
4826 wmId = LOWORD(wParam);
\r
4831 SAY("new game enter a move to play against the computer with white");
\r
4834 case IDM_NewGameFRC:
\r
4835 if( NewGameFRC() == 0 ) {
\r
4840 case IDM_NewVariant:
\r
4841 NewVariantPopup(hwnd);
\r
4844 case IDM_LoadGame:
\r
4845 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4848 case IDM_LoadNextGame:
\r
4852 case IDM_LoadPrevGame:
\r
4856 case IDM_ReloadGame:
\r
4860 case IDM_LoadPosition:
\r
4861 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4862 Reset(FALSE, TRUE);
\r
4865 f = OpenFileDialog(hwnd, "rb", "",
\r
4866 appData.oldSaveStyle ? "pos" : "fen",
\r
4868 _("Load Position from File"), &number, fileTitle, NULL);
\r
4870 LoadPosition(f, number, fileTitle);
\r
4874 case IDM_LoadNextPosition:
\r
4875 ReloadPosition(1);
\r
4878 case IDM_LoadPrevPosition:
\r
4879 ReloadPosition(-1);
\r
4882 case IDM_ReloadPosition:
\r
4883 ReloadPosition(0);
\r
4886 case IDM_SaveGame:
\r
4887 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4888 f = OpenFileDialog(hwnd, "a", defName,
\r
4889 appData.oldSaveStyle ? "gam" : "pgn",
\r
4891 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4893 SaveGame(f, 0, "");
\r
4897 case IDM_SavePosition:
\r
4898 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4899 f = OpenFileDialog(hwnd, "a", defName,
\r
4900 appData.oldSaveStyle ? "pos" : "fen",
\r
4902 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4904 SavePosition(f, 0, "");
\r
4908 case IDM_SaveDiagram:
\r
4909 defName = "diagram";
\r
4910 f = OpenFileDialog(hwnd, "wb", defName,
\r
4913 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4919 case IDM_CreateBook:
\r
4920 CreateBookEvent();
\r
4923 case IDM_CopyGame:
\r
4924 CopyGameToClipboard();
\r
4927 case IDM_PasteGame:
\r
4928 PasteGameFromClipboard();
\r
4931 case IDM_CopyGameListToClipboard:
\r
4932 CopyGameListToClipboard();
\r
4935 /* [AS] Autodetect FEN or PGN data */
\r
4936 case IDM_PasteAny:
\r
4937 PasteGameOrFENFromClipboard();
\r
4940 /* [AS] Move history */
\r
4941 case IDM_ShowMoveHistory:
\r
4942 if( MoveHistoryIsUp() ) {
\r
4943 MoveHistoryPopDown();
\r
4946 MoveHistoryPopUp();
\r
4950 /* [AS] Eval graph */
\r
4951 case IDM_ShowEvalGraph:
\r
4952 if( EvalGraphIsUp() ) {
\r
4953 EvalGraphPopDown();
\r
4957 SetFocus(hwndMain);
\r
4961 /* [AS] Engine output */
\r
4962 case IDM_ShowEngineOutput:
\r
4963 if( EngineOutputIsUp() ) {
\r
4964 EngineOutputPopDown();
\r
4967 EngineOutputPopUp();
\r
4971 /* [AS] User adjudication */
\r
4972 case IDM_UserAdjudication_White:
\r
4973 UserAdjudicationEvent( +1 );
\r
4976 case IDM_UserAdjudication_Black:
\r
4977 UserAdjudicationEvent( -1 );
\r
4980 case IDM_UserAdjudication_Draw:
\r
4981 UserAdjudicationEvent( 0 );
\r
4984 /* [AS] Game list options dialog */
\r
4985 case IDM_GameListOptions:
\r
4986 GameListOptions();
\r
4993 case IDM_CopyPosition:
\r
4994 CopyFENToClipboard();
\r
4997 case IDM_PastePosition:
\r
4998 PasteFENFromClipboard();
\r
5001 case IDM_MailMove:
\r
5005 case IDM_ReloadCMailMsg:
\r
5006 Reset(TRUE, TRUE);
\r
5007 ReloadCmailMsgEvent(FALSE);
\r
5010 case IDM_Minimize:
\r
5011 ShowWindow(hwnd, SW_MINIMIZE);
\r
5018 case IDM_MachineWhite:
\r
5019 MachineWhiteEvent();
\r
5021 * refresh the tags dialog only if it's visible
\r
5023 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5025 tags = PGNTags(&gameInfo);
\r
5026 TagsPopUp(tags, CmailMsg());
\r
5029 SAY("computer starts playing white");
\r
5032 case IDM_MachineBlack:
\r
5033 MachineBlackEvent();
\r
5035 * refresh the tags dialog only if it's visible
\r
5037 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5039 tags = PGNTags(&gameInfo);
\r
5040 TagsPopUp(tags, CmailMsg());
\r
5043 SAY("computer starts playing black");
\r
5046 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5047 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5050 case IDM_TwoMachines:
\r
5051 TwoMachinesEvent();
\r
5053 * refresh the tags dialog only if it's visible
\r
5055 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5057 tags = PGNTags(&gameInfo);
\r
5058 TagsPopUp(tags, CmailMsg());
\r
5061 SAY("computer starts playing both sides");
\r
5064 case IDM_AnalysisMode:
\r
5065 if(AnalyzeModeEvent()) {
\r
5066 SAY("analyzing current position");
\r
5070 case IDM_AnalyzeFile:
\r
5071 AnalyzeFileEvent();
\r
5074 case IDM_IcsClient:
\r
5078 case IDM_EditGame:
\r
5079 case IDM_EditGame2:
\r
5084 case IDM_EditPosition:
\r
5085 case IDM_EditPosition2:
\r
5086 EditPositionEvent();
\r
5087 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5090 case IDM_Training:
\r
5094 case IDM_ShowGameList:
\r
5095 ShowGameListProc();
\r
5098 case IDM_EditProgs1:
\r
5099 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5102 case IDM_LoadProg1:
\r
5103 LoadEnginePopUp(hwndMain, 0);
\r
5106 case IDM_LoadProg2:
\r
5107 LoadEnginePopUp(hwndMain, 1);
\r
5110 case IDM_EditServers:
\r
5111 EditTagsPopUp(icsNames, &icsNames);
\r
5114 case IDM_EditTags:
\r
5119 case IDM_EditBook:
\r
5123 case IDM_EditComment:
\r
5125 if (commentUp && editComment) {
\r
5128 EditCommentEvent();
\r
5149 case IDM_CallFlag:
\r
5169 case IDM_StopObserving:
\r
5170 StopObservingEvent();
\r
5173 case IDM_StopExamining:
\r
5174 StopExaminingEvent();
\r
5178 UploadGameEvent();
\r
5181 case IDM_TypeInMove:
\r
5182 TypeInEvent('\000');
\r
5185 case IDM_TypeInName:
\r
5186 PopUpNameDialog('\000');
\r
5189 case IDM_Backward:
\r
5191 SetFocus(hwndMain);
\r
5198 SetFocus(hwndMain);
\r
5203 SetFocus(hwndMain);
\r
5208 SetFocus(hwndMain);
\r
5211 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5212 case OPT_GameListPrev:
\r
5213 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5217 RevertEvent(FALSE);
\r
5220 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5221 RevertEvent(TRUE);
\r
5224 case IDM_TruncateGame:
\r
5225 TruncateGameEvent();
\r
5232 case IDM_RetractMove:
\r
5233 RetractMoveEvent();
\r
5236 case IDM_FlipView:
\r
5237 flipView = !flipView;
\r
5238 DrawPosition(FALSE, NULL);
\r
5241 case IDM_FlipClock:
\r
5242 flipClock = !flipClock;
\r
5243 DisplayBothClocks();
\r
5247 case IDM_MuteSounds:
\r
5248 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5249 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5250 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5253 case IDM_GeneralOptions:
\r
5254 GeneralOptionsPopup(hwnd);
\r
5255 DrawPosition(TRUE, NULL);
\r
5258 case IDM_BoardOptions:
\r
5259 BoardOptionsPopup(hwnd);
\r
5262 case IDM_ThemeOptions:
\r
5263 ThemeOptionsPopup(hwnd);
\r
5266 case IDM_EnginePlayOptions:
\r
5267 EnginePlayOptionsPopup(hwnd);
\r
5270 case IDM_Engine1Options:
\r
5271 EngineOptionsPopup(hwnd, &first);
\r
5274 case IDM_Engine2Options:
\r
5276 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5277 EngineOptionsPopup(hwnd, &second);
\r
5280 case IDM_OptionsUCI:
\r
5281 UciOptionsPopup(hwnd);
\r
5285 TourneyPopup(hwnd);
\r
5288 case IDM_IcsOptions:
\r
5289 IcsOptionsPopup(hwnd);
\r
5293 FontsOptionsPopup(hwnd);
\r
5297 SoundOptionsPopup(hwnd);
\r
5300 case IDM_CommPort:
\r
5301 CommPortOptionsPopup(hwnd);
\r
5304 case IDM_LoadOptions:
\r
5305 LoadOptionsPopup(hwnd);
\r
5308 case IDM_SaveOptions:
\r
5309 SaveOptionsPopup(hwnd);
\r
5312 case IDM_TimeControl:
\r
5313 TimeControlOptionsPopup(hwnd);
\r
5316 case IDM_SaveSettings:
\r
5317 SaveSettings(settingsFileName);
\r
5320 case IDM_SaveSettingsOnExit:
\r
5321 saveSettingsOnExit = !saveSettingsOnExit;
\r
5322 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5323 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5324 MF_CHECKED : MF_UNCHECKED));
\r
5335 case IDM_AboutGame:
\r
5340 appData.debugMode = !appData.debugMode;
\r
5341 if (appData.debugMode) {
\r
5342 char dir[MSG_SIZ];
\r
5343 GetCurrentDirectory(MSG_SIZ, dir);
\r
5344 SetCurrentDirectory(installDir);
\r
5345 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5346 SetCurrentDirectory(dir);
\r
5347 setbuf(debugFP, NULL);
\r
5354 case IDM_HELPCONTENTS:
\r
5355 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5356 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5357 MessageBox (GetFocus(),
\r
5358 _("Unable to activate help"),
\r
5359 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5363 case IDM_HELPSEARCH:
\r
5364 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5365 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5366 MessageBox (GetFocus(),
\r
5367 _("Unable to activate help"),
\r
5368 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5372 case IDM_HELPHELP:
\r
5373 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5374 MessageBox (GetFocus(),
\r
5375 _("Unable to activate help"),
\r
5376 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5381 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5383 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5384 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5385 FreeProcInstance(lpProc);
\r
5388 case IDM_DirectCommand1:
\r
5389 AskQuestionEvent(_("Direct Command"),
\r
5390 _("Send to chess program:"), "", "1");
\r
5392 case IDM_DirectCommand2:
\r
5393 AskQuestionEvent(_("Direct Command"),
\r
5394 _("Send to second chess program:"), "", "2");
\r
5397 case EP_WhitePawn:
\r
5398 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5402 case EP_WhiteKnight:
\r
5403 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5404 fromX = fromY = -1;
\r
5407 case EP_WhiteBishop:
\r
5408 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5409 fromX = fromY = -1;
\r
5412 case EP_WhiteRook:
\r
5413 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5414 fromX = fromY = -1;
\r
5417 case EP_WhiteQueen:
\r
5418 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5419 fromX = fromY = -1;
\r
5422 case EP_WhiteFerz:
\r
5423 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5424 fromX = fromY = -1;
\r
5427 case EP_WhiteWazir:
\r
5428 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5429 fromX = fromY = -1;
\r
5432 case EP_WhiteAlfil:
\r
5433 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5434 fromX = fromY = -1;
\r
5437 case EP_WhiteCannon:
\r
5438 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5439 fromX = fromY = -1;
\r
5442 case EP_WhiteCardinal:
\r
5443 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5444 fromX = fromY = -1;
\r
5447 case EP_WhiteMarshall:
\r
5448 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5449 fromX = fromY = -1;
\r
5452 case EP_WhiteKing:
\r
5453 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5454 fromX = fromY = -1;
\r
5457 case EP_BlackPawn:
\r
5458 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5459 fromX = fromY = -1;
\r
5462 case EP_BlackKnight:
\r
5463 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5464 fromX = fromY = -1;
\r
5467 case EP_BlackBishop:
\r
5468 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5469 fromX = fromY = -1;
\r
5472 case EP_BlackRook:
\r
5473 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5474 fromX = fromY = -1;
\r
5477 case EP_BlackQueen:
\r
5478 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5479 fromX = fromY = -1;
\r
5482 case EP_BlackFerz:
\r
5483 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5484 fromX = fromY = -1;
\r
5487 case EP_BlackWazir:
\r
5488 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5489 fromX = fromY = -1;
\r
5492 case EP_BlackAlfil:
\r
5493 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5494 fromX = fromY = -1;
\r
5497 case EP_BlackCannon:
\r
5498 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5499 fromX = fromY = -1;
\r
5502 case EP_BlackCardinal:
\r
5503 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5504 fromX = fromY = -1;
\r
5507 case EP_BlackMarshall:
\r
5508 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5509 fromX = fromY = -1;
\r
5512 case EP_BlackKing:
\r
5513 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5514 fromX = fromY = -1;
\r
5517 case EP_EmptySquare:
\r
5518 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5519 fromX = fromY = -1;
\r
5522 case EP_ClearBoard:
\r
5523 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5524 fromX = fromY = -1;
\r
5528 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5529 fromX = fromY = -1;
\r
5533 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5534 fromX = fromY = -1;
\r
5538 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5539 fromX = fromY = -1;
\r
5543 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5544 fromX = fromY = -1;
\r
5548 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5549 fromX = fromY = -1;
\r
5553 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5554 fromX = fromY = -1;
\r
5558 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5559 fromX = fromY = -1;
\r
5563 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5564 fromX = fromY = -1;
\r
5568 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5569 fromX = fromY = -1;
\r
5573 barbaric = 0; appData.language = "";
\r
5574 TranslateMenus(0);
\r
5575 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5576 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5577 lastChecked = wmId;
\r
5581 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5582 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5584 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5585 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5586 TranslateMenus(0);
\r
5587 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5588 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5589 lastChecked = wmId;
\r
5592 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5598 case CLOCK_TIMER_ID:
\r
5599 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5600 clockTimerEvent = 0;
\r
5601 DecrementClocks(); /* call into back end */
\r
5603 case LOAD_GAME_TIMER_ID:
\r
5604 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5605 loadGameTimerEvent = 0;
\r
5606 AutoPlayGameLoop(); /* call into back end */
\r
5608 case ANALYSIS_TIMER_ID:
\r
5609 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5610 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5611 AnalysisPeriodicEvent(0);
\r
5613 KillTimer(hwnd, analysisTimerEvent);
\r
5614 analysisTimerEvent = 0;
\r
5617 case DELAYED_TIMER_ID:
\r
5618 KillTimer(hwnd, delayedTimerEvent);
\r
5619 delayedTimerEvent = 0;
\r
5620 delayedTimerCallback();
\r
5625 case WM_USER_Input:
\r
5626 InputEvent(hwnd, message, wParam, lParam);
\r
5629 /* [AS] Also move "attached" child windows */
\r
5630 case WM_WINDOWPOSCHANGING:
\r
5632 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5633 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5635 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5636 /* Window is moving */
\r
5639 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5640 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5641 rcMain.right = wpMain.x + wpMain.width;
\r
5642 rcMain.top = wpMain.y;
\r
5643 rcMain.bottom = wpMain.y + wpMain.height;
\r
5645 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5646 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5647 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5648 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5649 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5650 wpMain.x = lpwp->x;
\r
5651 wpMain.y = lpwp->y;
\r
5656 /* [AS] Snapping */
\r
5657 case WM_ENTERSIZEMOVE:
\r
5658 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5659 if (hwnd == hwndMain) {
\r
5660 doingSizing = TRUE;
\r
5663 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5667 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5668 if (hwnd == hwndMain) {
\r
5669 lastSizing = wParam;
\r
5674 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5675 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5677 case WM_EXITSIZEMOVE:
\r
5678 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5679 if (hwnd == hwndMain) {
\r
5681 doingSizing = FALSE;
\r
5682 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5683 GetClientRect(hwnd, &client);
\r
5684 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5686 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5688 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5691 case WM_DESTROY: /* message: window being destroyed */
\r
5692 PostQuitMessage(0);
\r
5696 if (hwnd == hwndMain) {
\r
5701 default: /* Passes it on if unprocessed */
\r
5702 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5707 /*---------------------------------------------------------------------------*\
\r
5709 * Misc utility routines
\r
5711 \*---------------------------------------------------------------------------*/
\r
5714 * Decent random number generator, at least not as bad as Windows
\r
5715 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5717 unsigned int randstate;
\r
5722 randstate = randstate * 1664525 + 1013904223;
\r
5723 return (int) randstate & 0x7fffffff;
\r
5727 mysrandom(unsigned int seed)
\r
5734 * returns TRUE if user selects a different color, FALSE otherwise
\r
5738 ChangeColor(HWND hwnd, COLORREF *which)
\r
5740 static BOOL firstTime = TRUE;
\r
5741 static DWORD customColors[16];
\r
5743 COLORREF newcolor;
\r
5748 /* Make initial colors in use available as custom colors */
\r
5749 /* Should we put the compiled-in defaults here instead? */
\r
5751 customColors[i++] = lightSquareColor & 0xffffff;
\r
5752 customColors[i++] = darkSquareColor & 0xffffff;
\r
5753 customColors[i++] = whitePieceColor & 0xffffff;
\r
5754 customColors[i++] = blackPieceColor & 0xffffff;
\r
5755 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5756 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5758 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5759 customColors[i++] = textAttribs[ccl].color;
\r
5761 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5762 firstTime = FALSE;
\r
5765 cc.lStructSize = sizeof(cc);
\r
5766 cc.hwndOwner = hwnd;
\r
5767 cc.hInstance = NULL;
\r
5768 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5769 cc.lpCustColors = (LPDWORD) customColors;
\r
5770 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5772 if (!ChooseColor(&cc)) return FALSE;
\r
5774 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5775 if (newcolor == *which) return FALSE;
\r
5776 *which = newcolor;
\r
5780 InitDrawingColors();
\r
5781 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5786 MyLoadSound(MySound *ms)
\r
5792 if (ms->data && ms->flag) free(ms->data);
\r
5795 switch (ms->name[0]) {
\r
5801 /* System sound from Control Panel. Don't preload here. */
\r
5805 if (ms->name[1] == NULLCHAR) {
\r
5806 /* "!" alone = silence */
\r
5809 /* Builtin wave resource. Error if not found. */
\r
5810 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5811 if (h == NULL) break;
\r
5812 ms->data = (void *)LoadResource(hInst, h);
\r
5813 ms->flag = 0; // not maloced, so cannot be freed!
\r
5814 if (h == NULL) break;
\r
5819 /* .wav file. Error if not found. */
\r
5820 f = fopen(ms->name, "rb");
\r
5821 if (f == NULL) break;
\r
5822 if (fstat(fileno(f), &st) < 0) break;
\r
5823 ms->data = malloc(st.st_size);
\r
5825 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5831 char buf[MSG_SIZ];
\r
5832 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5833 DisplayError(buf, GetLastError());
\r
5839 MyPlaySound(MySound *ms)
\r
5841 BOOLEAN ok = FALSE;
\r
5843 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5844 switch (ms->name[0]) {
\r
5846 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5851 /* System sound from Control Panel (deprecated feature).
\r
5852 "$" alone or an unset sound name gets default beep (still in use). */
\r
5853 if (ms->name[1]) {
\r
5854 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5856 if (!ok) ok = MessageBeep(MB_OK);
\r
5859 /* Builtin wave resource, or "!" alone for silence */
\r
5860 if (ms->name[1]) {
\r
5861 if (ms->data == NULL) return FALSE;
\r
5862 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5868 /* .wav file. Error if not found. */
\r
5869 if (ms->data == NULL) return FALSE;
\r
5870 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5873 /* Don't print an error: this can happen innocently if the sound driver
\r
5874 is busy; for instance, if another instance of WinBoard is playing
\r
5875 a sound at about the same time. */
\r
5881 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5884 OPENFILENAME *ofn;
\r
5885 static UINT *number; /* gross that this is static */
\r
5887 switch (message) {
\r
5888 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5889 /* Center the dialog over the application window */
\r
5890 ofn = (OPENFILENAME *) lParam;
\r
5891 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5892 number = (UINT *) ofn->lCustData;
\r
5893 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5897 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5898 Translate(hDlg, 1536);
\r
5899 return FALSE; /* Allow for further processing */
\r
5902 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5903 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5905 return FALSE; /* Allow for further processing */
\r
5911 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5913 static UINT *number;
\r
5914 OPENFILENAME *ofname;
\r
5917 case WM_INITDIALOG:
\r
5918 Translate(hdlg, DLG_IndexNumber);
\r
5919 ofname = (OPENFILENAME *)lParam;
\r
5920 number = (UINT *)(ofname->lCustData);
\r
5923 ofnot = (OFNOTIFY *)lParam;
\r
5924 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5925 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5934 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5935 char *nameFilt, char *dlgTitle, UINT *number,
\r
5936 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5938 OPENFILENAME openFileName;
\r
5939 char buf1[MSG_SIZ];
\r
5942 if (fileName == NULL) fileName = buf1;
\r
5943 if (defName == NULL) {
\r
5944 safeStrCpy(fileName, "*.", 3 );
\r
5945 strcat(fileName, defExt);
\r
5947 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5949 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5950 if (number) *number = 0;
\r
5952 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5953 openFileName.hwndOwner = hwnd;
\r
5954 openFileName.hInstance = (HANDLE) hInst;
\r
5955 openFileName.lpstrFilter = nameFilt;
\r
5956 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5957 openFileName.nMaxCustFilter = 0L;
\r
5958 openFileName.nFilterIndex = 1L;
\r
5959 openFileName.lpstrFile = fileName;
\r
5960 openFileName.nMaxFile = MSG_SIZ;
\r
5961 openFileName.lpstrFileTitle = fileTitle;
\r
5962 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5963 openFileName.lpstrInitialDir = NULL;
\r
5964 openFileName.lpstrTitle = dlgTitle;
\r
5965 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5966 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5967 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5968 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5969 openFileName.nFileOffset = 0;
\r
5970 openFileName.nFileExtension = 0;
\r
5971 openFileName.lpstrDefExt = defExt;
\r
5972 openFileName.lCustData = (LONG) number;
\r
5973 openFileName.lpfnHook = oldDialog ?
\r
5974 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5975 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5977 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5978 GetOpenFileName(&openFileName)) {
\r
5979 /* open the file */
\r
5980 f = fopen(openFileName.lpstrFile, write);
\r
5982 MessageBox(hwnd, _("File open failed"), NULL,
\r
5983 MB_OK|MB_ICONEXCLAMATION);
\r
5987 int err = CommDlgExtendedError();
\r
5988 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5997 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5999 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6002 * Get the first pop-up menu in the menu template. This is the
\r
6003 * menu that TrackPopupMenu displays.
\r
6005 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6006 TranslateOneMenu(10, hmenuTrackPopup);
\r
6008 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6011 * TrackPopup uses screen coordinates, so convert the
\r
6012 * coordinates of the mouse click to screen coordinates.
\r
6014 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6016 /* Draw and track the floating pop-up menu. */
\r
6017 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6018 pt.x, pt.y, 0, hwnd, NULL);
\r
6020 /* Destroy the menu.*/
\r
6021 DestroyMenu(hmenu);
\r
6026 int sizeX, sizeY, newSizeX, newSizeY;
\r
6028 } ResizeEditPlusButtonsClosure;
\r
6031 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6033 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6037 if (hChild == cl->hText) return TRUE;
\r
6038 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6039 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6040 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6041 ScreenToClient(cl->hDlg, &pt);
\r
6042 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6043 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6047 /* Resize a dialog that has a (rich) edit field filling most of
\r
6048 the top, with a row of buttons below */
\r
6050 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6053 int newTextHeight, newTextWidth;
\r
6054 ResizeEditPlusButtonsClosure cl;
\r
6056 /*if (IsIconic(hDlg)) return;*/
\r
6057 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6059 cl.hdwp = BeginDeferWindowPos(8);
\r
6061 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6062 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6063 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6064 if (newTextHeight < 0) {
\r
6065 newSizeY += -newTextHeight;
\r
6066 newTextHeight = 0;
\r
6068 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6069 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6075 cl.newSizeX = newSizeX;
\r
6076 cl.newSizeY = newSizeY;
\r
6077 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6079 EndDeferWindowPos(cl.hdwp);
\r
6082 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6084 RECT rChild, rParent;
\r
6085 int wChild, hChild, wParent, hParent;
\r
6086 int wScreen, hScreen, xNew, yNew;
\r
6089 /* Get the Height and Width of the child window */
\r
6090 GetWindowRect (hwndChild, &rChild);
\r
6091 wChild = rChild.right - rChild.left;
\r
6092 hChild = rChild.bottom - rChild.top;
\r
6094 /* Get the Height and Width of the parent window */
\r
6095 GetWindowRect (hwndParent, &rParent);
\r
6096 wParent = rParent.right - rParent.left;
\r
6097 hParent = rParent.bottom - rParent.top;
\r
6099 /* Get the display limits */
\r
6100 hdc = GetDC (hwndChild);
\r
6101 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6102 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6103 ReleaseDC(hwndChild, hdc);
\r
6105 /* Calculate new X position, then adjust for screen */
\r
6106 xNew = rParent.left + ((wParent - wChild) /2);
\r
6109 } else if ((xNew+wChild) > wScreen) {
\r
6110 xNew = wScreen - wChild;
\r
6113 /* Calculate new Y position, then adjust for screen */
\r
6115 yNew = rParent.top + ((hParent - hChild) /2);
\r
6118 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6123 } else if ((yNew+hChild) > hScreen) {
\r
6124 yNew = hScreen - hChild;
\r
6127 /* Set it, and return */
\r
6128 return SetWindowPos (hwndChild, NULL,
\r
6129 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6132 /* Center one window over another */
\r
6133 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6135 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6138 /*---------------------------------------------------------------------------*\
\r
6140 * Startup Dialog functions
\r
6142 \*---------------------------------------------------------------------------*/
\r
6144 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6146 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6148 while (*cd != NULL) {
\r
6149 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6155 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6157 char buf1[MAX_ARG_LEN];
\r
6160 if (str[0] == '@') {
\r
6161 FILE* f = fopen(str + 1, "r");
\r
6163 DisplayFatalError(str + 1, errno, 2);
\r
6166 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6168 buf1[len] = NULLCHAR;
\r
6172 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6175 char buf[MSG_SIZ];
\r
6176 char *end = strchr(str, '\n');
\r
6177 if (end == NULL) return;
\r
6178 memcpy(buf, str, end - str);
\r
6179 buf[end - str] = NULLCHAR;
\r
6180 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6186 SetStartupDialogEnables(HWND hDlg)
\r
6188 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6189 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6190 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6191 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6192 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6193 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6194 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6195 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6196 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6197 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6198 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6199 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6200 IsDlgButtonChecked(hDlg, OPT_View));
\r
6204 QuoteForFilename(char *filename)
\r
6206 int dquote, space;
\r
6207 dquote = strchr(filename, '"') != NULL;
\r
6208 space = strchr(filename, ' ') != NULL;
\r
6209 if (dquote || space) {
\r
6221 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6223 char buf[MSG_SIZ];
\r
6226 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6227 q = QuoteForFilename(nthcp);
\r
6228 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6229 if (*nthdir != NULLCHAR) {
\r
6230 q = QuoteForFilename(nthdir);
\r
6231 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6233 if (*nthcp == NULLCHAR) {
\r
6234 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6235 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6236 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6237 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6242 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6244 char buf[MSG_SIZ];
\r
6248 switch (message) {
\r
6249 case WM_INITDIALOG:
\r
6250 /* Center the dialog */
\r
6251 CenterWindow (hDlg, GetDesktopWindow());
\r
6252 Translate(hDlg, DLG_Startup);
\r
6253 /* Initialize the dialog items */
\r
6254 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6255 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6256 firstChessProgramNames);
\r
6257 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6258 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6259 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6260 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6261 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6262 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6263 if (*appData.icsHelper != NULLCHAR) {
\r
6264 char *q = QuoteForFilename(appData.icsHelper);
\r
6265 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6267 if (*appData.icsHost == NULLCHAR) {
\r
6268 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6269 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6270 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6271 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6272 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6275 if (appData.icsActive) {
\r
6276 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6278 else if (appData.noChessProgram) {
\r
6279 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6282 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6285 SetStartupDialogEnables(hDlg);
\r
6289 switch (LOWORD(wParam)) {
\r
6291 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6292 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6293 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6295 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6296 ParseArgs(StringGet, &p);
\r
6297 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6298 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6300 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6301 ParseArgs(StringGet, &p);
\r
6302 SwapEngines(singleList); // ... and then make it 'second'
\r
6304 appData.noChessProgram = FALSE;
\r
6305 appData.icsActive = FALSE;
\r
6306 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6307 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6308 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6310 ParseArgs(StringGet, &p);
\r
6311 if (appData.zippyPlay) {
\r
6312 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6313 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6315 ParseArgs(StringGet, &p);
\r
6317 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6318 appData.noChessProgram = TRUE;
\r
6319 appData.icsActive = FALSE;
\r
6321 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6322 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6325 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6326 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6328 ParseArgs(StringGet, &p);
\r
6330 EndDialog(hDlg, TRUE);
\r
6337 case IDM_HELPCONTENTS:
\r
6338 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6339 MessageBox (GetFocus(),
\r
6340 _("Unable to activate help"),
\r
6341 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6346 SetStartupDialogEnables(hDlg);
\r
6354 /*---------------------------------------------------------------------------*\
\r
6356 * About box dialog functions
\r
6358 \*---------------------------------------------------------------------------*/
\r
6360 /* Process messages for "About" dialog box */
\r
6362 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6364 switch (message) {
\r
6365 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6366 /* Center the dialog over the application window */
\r
6367 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6368 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6369 Translate(hDlg, ABOUTBOX);
\r
6373 case WM_COMMAND: /* message: received a command */
\r
6374 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6375 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6376 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6384 /*---------------------------------------------------------------------------*\
\r
6386 * Comment Dialog functions
\r
6388 \*---------------------------------------------------------------------------*/
\r
6391 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6393 static HANDLE hwndText = NULL;
\r
6394 int len, newSizeX, newSizeY;
\r
6395 static int sizeX, sizeY;
\r
6400 switch (message) {
\r
6401 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6402 /* Initialize the dialog items */
\r
6403 Translate(hDlg, DLG_EditComment);
\r
6404 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6405 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6406 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6407 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6408 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6409 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6410 SetWindowText(hDlg, commentTitle);
\r
6411 if (editComment) {
\r
6412 SetFocus(hwndText);
\r
6414 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6416 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6417 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6418 MAKELPARAM(FALSE, 0));
\r
6419 /* Size and position the dialog */
\r
6420 if (!commentDialog) {
\r
6421 commentDialog = hDlg;
\r
6422 GetClientRect(hDlg, &rect);
\r
6423 sizeX = rect.right;
\r
6424 sizeY = rect.bottom;
\r
6425 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6426 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6427 WINDOWPLACEMENT wp;
\r
6428 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6429 wp.length = sizeof(WINDOWPLACEMENT);
\r
6431 wp.showCmd = SW_SHOW;
\r
6432 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6433 wp.rcNormalPosition.left = wpComment.x;
\r
6434 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6435 wp.rcNormalPosition.top = wpComment.y;
\r
6436 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6437 SetWindowPlacement(hDlg, &wp);
\r
6439 GetClientRect(hDlg, &rect);
\r
6440 newSizeX = rect.right;
\r
6441 newSizeY = rect.bottom;
\r
6442 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6443 newSizeX, newSizeY);
\r
6448 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6451 case WM_COMMAND: /* message: received a command */
\r
6452 switch (LOWORD(wParam)) {
\r
6454 if (editComment) {
\r
6456 /* Read changed options from the dialog box */
\r
6457 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6458 len = GetWindowTextLength(hwndText);
\r
6459 str = (char *) malloc(len + 1);
\r
6460 GetWindowText(hwndText, str, len + 1);
\r
6469 ReplaceComment(commentIndex, str);
\r
6476 case OPT_CancelComment:
\r
6480 case OPT_ClearComment:
\r
6481 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6484 case OPT_EditComment:
\r
6485 EditCommentEvent();
\r
6493 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6494 if( wParam == OPT_CommentText ) {
\r
6495 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6497 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6498 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6502 pt.x = LOWORD( lpMF->lParam );
\r
6503 pt.y = HIWORD( lpMF->lParam );
\r
6505 if(lpMF->msg == WM_CHAR) {
\r
6507 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6508 index = sel.cpMin;
\r
6510 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6512 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6513 len = GetWindowTextLength(hwndText);
\r
6514 str = (char *) malloc(len + 1);
\r
6515 GetWindowText(hwndText, str, len + 1);
\r
6516 ReplaceComment(commentIndex, str);
\r
6517 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6518 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6521 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6522 lpMF->msg = WM_USER;
\r
6530 newSizeX = LOWORD(lParam);
\r
6531 newSizeY = HIWORD(lParam);
\r
6532 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6537 case WM_GETMINMAXINFO:
\r
6538 /* Prevent resizing window too small */
\r
6539 mmi = (MINMAXINFO *) lParam;
\r
6540 mmi->ptMinTrackSize.x = 100;
\r
6541 mmi->ptMinTrackSize.y = 100;
\r
6548 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6553 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6555 if (str == NULL) str = "";
\r
6556 p = (char *) malloc(2 * strlen(str) + 2);
\r
6559 if (*str == '\n') *q++ = '\r';
\r
6563 if (commentText != NULL) free(commentText);
\r
6565 commentIndex = index;
\r
6566 commentTitle = title;
\r
6568 editComment = edit;
\r
6570 if (commentDialog) {
\r
6571 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6572 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6574 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6575 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6576 hwndMain, (DLGPROC)lpProc);
\r
6577 FreeProcInstance(lpProc);
\r
6583 /*---------------------------------------------------------------------------*\
\r
6585 * Type-in move dialog functions
\r
6587 \*---------------------------------------------------------------------------*/
\r
6590 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6592 char move[MSG_SIZ];
\r
6595 switch (message) {
\r
6596 case WM_INITDIALOG:
\r
6597 move[0] = (char) lParam;
\r
6598 move[1] = NULLCHAR;
\r
6599 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6600 Translate(hDlg, DLG_TypeInMove);
\r
6601 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6602 SetWindowText(hInput, move);
\r
6604 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6608 switch (LOWORD(wParam)) {
\r
6611 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6612 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6613 TypeInDoneEvent(move);
\r
6614 EndDialog(hDlg, TRUE);
\r
6617 EndDialog(hDlg, FALSE);
\r
6628 PopUpMoveDialog(char firstchar)
\r
6632 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6633 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6634 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6635 FreeProcInstance(lpProc);
\r
6638 /*---------------------------------------------------------------------------*\
\r
6640 * Type-in name dialog functions
\r
6642 \*---------------------------------------------------------------------------*/
\r
6645 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6647 char move[MSG_SIZ];
\r
6650 switch (message) {
\r
6651 case WM_INITDIALOG:
\r
6652 move[0] = (char) lParam;
\r
6653 move[1] = NULLCHAR;
\r
6654 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6655 Translate(hDlg, DLG_TypeInName);
\r
6656 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6657 SetWindowText(hInput, move);
\r
6659 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6663 switch (LOWORD(wParam)) {
\r
6665 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6666 appData.userName = strdup(move);
\r
6669 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6670 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6671 DisplayTitle(move);
\r
6675 EndDialog(hDlg, TRUE);
\r
6678 EndDialog(hDlg, FALSE);
\r
6689 PopUpNameDialog(char firstchar)
\r
6693 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6694 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6695 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6696 FreeProcInstance(lpProc);
\r
6699 /*---------------------------------------------------------------------------*\
\r
6703 \*---------------------------------------------------------------------------*/
\r
6705 /* Nonmodal error box */
\r
6706 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6707 WPARAM wParam, LPARAM lParam);
\r
6710 ErrorPopUp(char *title, char *content)
\r
6714 BOOLEAN modal = hwndMain == NULL;
\r
6732 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6733 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6736 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6738 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6739 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6740 hwndMain, (DLGPROC)lpProc);
\r
6741 FreeProcInstance(lpProc);
\r
6748 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6749 if (errorDialog == NULL) return;
\r
6750 DestroyWindow(errorDialog);
\r
6751 errorDialog = NULL;
\r
6752 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6756 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6760 switch (message) {
\r
6761 case WM_INITDIALOG:
\r
6762 GetWindowRect(hDlg, &rChild);
\r
6765 SetWindowPos(hDlg, NULL, rChild.left,
\r
6766 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6767 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6771 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6772 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6773 and it doesn't work when you resize the dialog.
\r
6774 For now, just give it a default position.
\r
6776 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6777 Translate(hDlg, DLG_Error);
\r
6779 errorDialog = hDlg;
\r
6780 SetWindowText(hDlg, errorTitle);
\r
6781 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6785 switch (LOWORD(wParam)) {
\r
6788 if (errorDialog == hDlg) errorDialog = NULL;
\r
6789 DestroyWindow(hDlg);
\r
6801 HWND gothicDialog = NULL;
\r
6804 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6807 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6809 switch (message) {
\r
6810 case WM_INITDIALOG:
\r
6811 GetWindowRect(hDlg, &rChild);
\r
6813 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6817 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6818 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6819 and it doesn't work when you resize the dialog.
\r
6820 For now, just give it a default position.
\r
6822 gothicDialog = hDlg;
\r
6823 SetWindowText(hDlg, errorTitle);
\r
6824 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6828 switch (LOWORD(wParam)) {
\r
6831 if (errorDialog == hDlg) errorDialog = NULL;
\r
6832 DestroyWindow(hDlg);
\r
6844 GothicPopUp(char *title, VariantClass variant)
\r
6847 static char *lastTitle;
\r
6849 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6850 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6852 if(lastTitle != title && gothicDialog != NULL) {
\r
6853 DestroyWindow(gothicDialog);
\r
6854 gothicDialog = NULL;
\r
6856 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6857 title = lastTitle;
\r
6858 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6859 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6860 hwndMain, (DLGPROC)lpProc);
\r
6861 FreeProcInstance(lpProc);
\r
6866 /*---------------------------------------------------------------------------*\
\r
6868 * Ics Interaction console functions
\r
6870 \*---------------------------------------------------------------------------*/
\r
6872 #define HISTORY_SIZE 64
\r
6873 static char *history[HISTORY_SIZE];
\r
6874 int histIn = 0, histP = 0;
\r
6878 SaveInHistory(char *cmd)
\r
6880 if (history[histIn] != NULL) {
\r
6881 free(history[histIn]);
\r
6882 history[histIn] = NULL;
\r
6884 if (*cmd == NULLCHAR) return;
\r
6885 history[histIn] = StrSave(cmd);
\r
6886 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6887 if (history[histIn] != NULL) {
\r
6888 free(history[histIn]);
\r
6890 history[histIn] = NULL;
\r
6896 PrevInHistory(char *cmd)
\r
6899 if (histP == histIn) {
\r
6900 if (history[histIn] != NULL) free(history[histIn]);
\r
6901 history[histIn] = StrSave(cmd);
\r
6903 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6904 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6906 return history[histP];
\r
6912 if (histP == histIn) return NULL;
\r
6913 histP = (histP + 1) % HISTORY_SIZE;
\r
6914 return history[histP];
\r
6918 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6922 hmenu = LoadMenu(hInst, "TextMenu");
\r
6923 h = GetSubMenu(hmenu, 0);
\r
6925 if (strcmp(e->item, "-") == 0) {
\r
6926 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6927 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6928 int flags = MF_STRING, j = 0;
\r
6929 if (e->item[0] == '|') {
\r
6930 flags |= MF_MENUBARBREAK;
\r
6933 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6934 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6942 WNDPROC consoleTextWindowProc;
\r
6945 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6947 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6948 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6952 SetWindowText(hInput, command);
\r
6954 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6956 sel.cpMin = 999999;
\r
6957 sel.cpMax = 999999;
\r
6958 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6963 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6964 if (sel.cpMin == sel.cpMax) {
\r
6965 /* Expand to surrounding word */
\r
6968 tr.chrg.cpMax = sel.cpMin;
\r
6969 tr.chrg.cpMin = --sel.cpMin;
\r
6970 if (sel.cpMin < 0) break;
\r
6971 tr.lpstrText = name;
\r
6972 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6973 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6977 tr.chrg.cpMin = sel.cpMax;
\r
6978 tr.chrg.cpMax = ++sel.cpMax;
\r
6979 tr.lpstrText = name;
\r
6980 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6981 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6984 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6985 MessageBeep(MB_ICONEXCLAMATION);
\r
6989 tr.lpstrText = name;
\r
6990 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6992 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6993 MessageBeep(MB_ICONEXCLAMATION);
\r
6996 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6999 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7000 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7001 SetWindowText(hInput, buf);
\r
7002 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7004 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7005 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7006 SetWindowText(hInput, buf);
\r
7007 sel.cpMin = 999999;
\r
7008 sel.cpMax = 999999;
\r
7009 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7015 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7020 switch (message) {
\r
7022 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7023 if(wParam=='R') return 0;
\r
7026 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7029 sel.cpMin = 999999;
\r
7030 sel.cpMax = 999999;
\r
7031 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7032 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7037 if(wParam != '\022') {
\r
7038 if (wParam == '\t') {
\r
7039 if (GetKeyState(VK_SHIFT) < 0) {
\r
7041 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7042 if (buttonDesc[0].hwnd) {
\r
7043 SetFocus(buttonDesc[0].hwnd);
\r
7045 SetFocus(hwndMain);
\r
7049 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7052 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7053 JAWS_DELETE( SetFocus(hInput); )
\r
7054 SendMessage(hInput, message, wParam, lParam);
\r
7057 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7059 case WM_RBUTTONDOWN:
\r
7060 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7061 /* Move selection here if it was empty */
\r
7063 pt.x = LOWORD(lParam);
\r
7064 pt.y = HIWORD(lParam);
\r
7065 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7066 if (sel.cpMin == sel.cpMax) {
\r
7067 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7068 sel.cpMax = sel.cpMin;
\r
7069 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7071 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7072 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7074 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7075 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7076 if (sel.cpMin == sel.cpMax) {
\r
7077 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7078 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7080 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7081 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7083 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7084 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7085 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7086 MenuPopup(hwnd, pt, hmenu, -1);
\r
7090 case WM_RBUTTONUP:
\r
7091 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7092 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7093 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7097 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7099 return SendMessage(hInput, message, wParam, lParam);
\r
7100 case WM_MBUTTONDOWN:
\r
7101 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7103 switch (LOWORD(wParam)) {
\r
7104 case IDM_QuickPaste:
\r
7106 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7107 if (sel.cpMin == sel.cpMax) {
\r
7108 MessageBeep(MB_ICONEXCLAMATION);
\r
7111 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7112 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7113 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7118 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7121 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7124 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7128 int i = LOWORD(wParam) - IDM_CommandX;
\r
7129 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7130 icsTextMenuEntry[i].command != NULL) {
\r
7131 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7132 icsTextMenuEntry[i].getname,
\r
7133 icsTextMenuEntry[i].immediate);
\r
7141 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7144 WNDPROC consoleInputWindowProc;
\r
7147 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7149 char buf[MSG_SIZ];
\r
7151 static BOOL sendNextChar = FALSE;
\r
7152 static BOOL quoteNextChar = FALSE;
\r
7153 InputSource *is = consoleInputSource;
\r
7157 switch (message) {
\r
7159 if (!appData.localLineEditing || sendNextChar) {
\r
7160 is->buf[0] = (CHAR) wParam;
\r
7162 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7163 sendNextChar = FALSE;
\r
7166 if (quoteNextChar) {
\r
7167 buf[0] = (char) wParam;
\r
7168 buf[1] = NULLCHAR;
\r
7169 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7170 quoteNextChar = FALSE;
\r
7174 case '\r': /* Enter key */
\r
7175 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7176 if (consoleEcho) SaveInHistory(is->buf);
\r
7177 is->buf[is->count++] = '\n';
\r
7178 is->buf[is->count] = NULLCHAR;
\r
7179 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7180 if (consoleEcho) {
\r
7181 ConsoleOutput(is->buf, is->count, TRUE);
\r
7182 } else if (appData.localLineEditing) {
\r
7183 ConsoleOutput("\n", 1, TRUE);
\r
7186 case '\033': /* Escape key */
\r
7187 SetWindowText(hwnd, "");
\r
7188 cf.cbSize = sizeof(CHARFORMAT);
\r
7189 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7190 if (consoleEcho) {
\r
7191 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7193 cf.crTextColor = COLOR_ECHOOFF;
\r
7195 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7196 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7198 case '\t': /* Tab key */
\r
7199 if (GetKeyState(VK_SHIFT) < 0) {
\r
7201 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7204 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7205 if (buttonDesc[0].hwnd) {
\r
7206 SetFocus(buttonDesc[0].hwnd);
\r
7208 SetFocus(hwndMain);
\r
7212 case '\023': /* Ctrl+S */
\r
7213 sendNextChar = TRUE;
\r
7215 case '\021': /* Ctrl+Q */
\r
7216 quoteNextChar = TRUE;
\r
7226 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7227 p = PrevInHistory(buf);
\r
7229 SetWindowText(hwnd, p);
\r
7230 sel.cpMin = 999999;
\r
7231 sel.cpMax = 999999;
\r
7232 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7237 p = NextInHistory();
\r
7239 SetWindowText(hwnd, p);
\r
7240 sel.cpMin = 999999;
\r
7241 sel.cpMax = 999999;
\r
7242 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7248 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7252 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7256 case WM_MBUTTONDOWN:
\r
7257 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7258 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7260 case WM_RBUTTONUP:
\r
7261 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7262 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7263 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7267 hmenu = LoadMenu(hInst, "InputMenu");
\r
7268 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7269 if (sel.cpMin == sel.cpMax) {
\r
7270 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7271 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7273 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7274 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7276 pt.x = LOWORD(lParam);
\r
7277 pt.y = HIWORD(lParam);
\r
7278 MenuPopup(hwnd, pt, hmenu, -1);
\r
7282 switch (LOWORD(wParam)) {
\r
7284 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7286 case IDM_SelectAll:
\r
7288 sel.cpMax = -1; /*999999?*/
\r
7289 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7292 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7295 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7298 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7303 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7306 #define CO_MAX 100000
\r
7307 #define CO_TRIM 1000
\r
7310 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7312 static SnapData sd;
\r
7313 HWND hText, hInput;
\r
7315 static int sizeX, sizeY;
\r
7316 int newSizeX, newSizeY;
\r
7320 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7321 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7323 switch (message) {
\r
7325 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7327 ENLINK *pLink = (ENLINK*)lParam;
\r
7328 if (pLink->msg == WM_LBUTTONUP)
\r
7332 tr.chrg = pLink->chrg;
\r
7333 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7334 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7335 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7336 free(tr.lpstrText);
\r
7340 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7341 hwndConsole = hDlg;
\r
7343 consoleTextWindowProc = (WNDPROC)
\r
7344 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7345 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7346 consoleInputWindowProc = (WNDPROC)
\r
7347 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7348 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7349 Colorize(ColorNormal, TRUE);
\r
7350 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7351 ChangedConsoleFont();
\r
7352 GetClientRect(hDlg, &rect);
\r
7353 sizeX = rect.right;
\r
7354 sizeY = rect.bottom;
\r
7355 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7356 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7357 WINDOWPLACEMENT wp;
\r
7358 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7359 wp.length = sizeof(WINDOWPLACEMENT);
\r
7361 wp.showCmd = SW_SHOW;
\r
7362 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7363 wp.rcNormalPosition.left = wpConsole.x;
\r
7364 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7365 wp.rcNormalPosition.top = wpConsole.y;
\r
7366 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7367 SetWindowPlacement(hDlg, &wp);
\r
7370 // [HGM] Chessknight's change 2004-07-13
\r
7371 else { /* Determine Defaults */
\r
7372 WINDOWPLACEMENT wp;
\r
7373 wpConsole.x = wpMain.width + 1;
\r
7374 wpConsole.y = wpMain.y;
\r
7375 wpConsole.width = screenWidth - wpMain.width;
\r
7376 wpConsole.height = wpMain.height;
\r
7377 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7378 wp.length = sizeof(WINDOWPLACEMENT);
\r
7380 wp.showCmd = SW_SHOW;
\r
7381 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7382 wp.rcNormalPosition.left = wpConsole.x;
\r
7383 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7384 wp.rcNormalPosition.top = wpConsole.y;
\r
7385 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7386 SetWindowPlacement(hDlg, &wp);
\r
7389 // Allow hText to highlight URLs and send notifications on them
\r
7390 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7391 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7392 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7393 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7407 if (IsIconic(hDlg)) break;
\r
7408 newSizeX = LOWORD(lParam);
\r
7409 newSizeY = HIWORD(lParam);
\r
7410 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7411 RECT rectText, rectInput;
\r
7413 int newTextHeight, newTextWidth;
\r
7414 GetWindowRect(hText, &rectText);
\r
7415 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7416 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7417 if (newTextHeight < 0) {
\r
7418 newSizeY += -newTextHeight;
\r
7419 newTextHeight = 0;
\r
7421 SetWindowPos(hText, NULL, 0, 0,
\r
7422 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7423 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7424 pt.x = rectInput.left;
\r
7425 pt.y = rectInput.top + newSizeY - sizeY;
\r
7426 ScreenToClient(hDlg, &pt);
\r
7427 SetWindowPos(hInput, NULL,
\r
7428 pt.x, pt.y, /* needs client coords */
\r
7429 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7430 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7436 case WM_GETMINMAXINFO:
\r
7437 /* Prevent resizing window too small */
\r
7438 mmi = (MINMAXINFO *) lParam;
\r
7439 mmi->ptMinTrackSize.x = 100;
\r
7440 mmi->ptMinTrackSize.y = 100;
\r
7443 /* [AS] Snapping */
\r
7444 case WM_ENTERSIZEMOVE:
\r
7445 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7448 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7451 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7453 case WM_EXITSIZEMOVE:
\r
7454 UpdateICSWidth(hText);
\r
7455 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7458 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7466 if (hwndConsole) return;
\r
7467 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7468 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7473 ConsoleOutput(char* data, int length, int forceVisible)
\r
7478 char buf[CO_MAX+1];
\r
7481 static int delayLF = 0;
\r
7482 CHARRANGE savesel, sel;
\r
7484 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7492 while (length--) {
\r
7500 } else if (*p == '\007') {
\r
7501 MyPlaySound(&sounds[(int)SoundBell]);
\r
7508 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7509 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7510 /* Save current selection */
\r
7511 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7512 exlen = GetWindowTextLength(hText);
\r
7513 /* Find out whether current end of text is visible */
\r
7514 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7515 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7516 /* Trim existing text if it's too long */
\r
7517 if (exlen + (q - buf) > CO_MAX) {
\r
7518 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7521 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7522 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7524 savesel.cpMin -= trim;
\r
7525 savesel.cpMax -= trim;
\r
7526 if (exlen < 0) exlen = 0;
\r
7527 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7528 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7530 /* Append the new text */
\r
7531 sel.cpMin = exlen;
\r
7532 sel.cpMax = exlen;
\r
7533 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7534 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7535 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7536 if (forceVisible || exlen == 0 ||
\r
7537 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7538 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7539 /* Scroll to make new end of text visible if old end of text
\r
7540 was visible or new text is an echo of user typein */
\r
7541 sel.cpMin = 9999999;
\r
7542 sel.cpMax = 9999999;
\r
7543 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7544 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7545 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7546 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7548 if (savesel.cpMax == exlen || forceVisible) {
\r
7549 /* Move insert point to new end of text if it was at the old
\r
7550 end of text or if the new text is an echo of user typein */
\r
7551 sel.cpMin = 9999999;
\r
7552 sel.cpMax = 9999999;
\r
7553 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7555 /* Restore previous selection */
\r
7556 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7558 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7565 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7569 COLORREF oldFg, oldBg;
\r
7573 if(copyNumber > 1)
\r
7574 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7576 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7577 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7578 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7581 rect.right = x + squareSize;
\r
7583 rect.bottom = y + squareSize;
\r
7586 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7587 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7588 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7589 &rect, str, strlen(str), NULL);
\r
7591 (void) SetTextColor(hdc, oldFg);
\r
7592 (void) SetBkColor(hdc, oldBg);
\r
7593 (void) SelectObject(hdc, oldFont);
\r
7597 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7598 RECT *rect, char *color, char *flagFell)
\r
7602 COLORREF oldFg, oldBg;
\r
7605 if (twoBoards && partnerUp) return;
\r
7606 if (appData.clockMode) {
\r
7608 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7610 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7617 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7618 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7620 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7621 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7623 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7627 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7628 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7629 rect, str, strlen(str), NULL);
\r
7630 if(logoHeight > 0 && appData.clockMode) {
\r
7632 str += strlen(color)+2;
\r
7633 r.top = rect->top + logoHeight/2;
\r
7634 r.left = rect->left;
\r
7635 r.right = rect->right;
\r
7636 r.bottom = rect->bottom;
\r
7637 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7638 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7639 &r, str, strlen(str), NULL);
\r
7641 (void) SetTextColor(hdc, oldFg);
\r
7642 (void) SetBkColor(hdc, oldBg);
\r
7643 (void) SelectObject(hdc, oldFont);
\r
7648 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7654 if( count <= 0 ) {
\r
7655 if (appData.debugMode) {
\r
7656 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7659 return ERROR_INVALID_USER_BUFFER;
\r
7662 ResetEvent(ovl->hEvent);
\r
7663 ovl->Offset = ovl->OffsetHigh = 0;
\r
7664 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7668 err = GetLastError();
\r
7669 if (err == ERROR_IO_PENDING) {
\r
7670 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7674 err = GetLastError();
\r
7681 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7686 ResetEvent(ovl->hEvent);
\r
7687 ovl->Offset = ovl->OffsetHigh = 0;
\r
7688 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7692 err = GetLastError();
\r
7693 if (err == ERROR_IO_PENDING) {
\r
7694 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7698 err = GetLastError();
\r
7705 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7706 void CheckForInputBufferFull( InputSource * is )
\r
7708 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7709 /* Look for end of line */
\r
7710 char * p = is->buf;
\r
7712 while( p < is->next && *p != '\n' ) {
\r
7716 if( p >= is->next ) {
\r
7717 if (appData.debugMode) {
\r
7718 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7721 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7722 is->count = (DWORD) -1;
\r
7723 is->next = is->buf;
\r
7729 InputThread(LPVOID arg)
\r
7734 is = (InputSource *) arg;
\r
7735 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7736 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7737 while (is->hThread != NULL) {
\r
7738 is->error = DoReadFile(is->hFile, is->next,
\r
7739 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7740 &is->count, &ovl);
\r
7741 if (is->error == NO_ERROR) {
\r
7742 is->next += is->count;
\r
7744 if (is->error == ERROR_BROKEN_PIPE) {
\r
7745 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7748 is->count = (DWORD) -1;
\r
7749 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7754 CheckForInputBufferFull( is );
\r
7756 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7758 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7760 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7763 CloseHandle(ovl.hEvent);
\r
7764 CloseHandle(is->hFile);
\r
7766 if (appData.debugMode) {
\r
7767 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7774 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7776 NonOvlInputThread(LPVOID arg)
\r
7783 is = (InputSource *) arg;
\r
7784 while (is->hThread != NULL) {
\r
7785 is->error = ReadFile(is->hFile, is->next,
\r
7786 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7787 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7788 if (is->error == NO_ERROR) {
\r
7789 /* Change CRLF to LF */
\r
7790 if (is->next > is->buf) {
\r
7792 i = is->count + 1;
\r
7800 if (prev == '\r' && *p == '\n') {
\r
7812 if (is->error == ERROR_BROKEN_PIPE) {
\r
7813 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7816 is->count = (DWORD) -1;
\r
7820 CheckForInputBufferFull( is );
\r
7822 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7824 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7826 if (is->count < 0) break; /* Quit on error */
\r
7828 CloseHandle(is->hFile);
\r
7833 SocketInputThread(LPVOID arg)
\r
7837 is = (InputSource *) arg;
\r
7838 while (is->hThread != NULL) {
\r
7839 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7840 if ((int)is->count == SOCKET_ERROR) {
\r
7841 is->count = (DWORD) -1;
\r
7842 is->error = WSAGetLastError();
\r
7844 is->error = NO_ERROR;
\r
7845 is->next += is->count;
\r
7846 if (is->count == 0 && is->second == is) {
\r
7847 /* End of file on stderr; quit with no message */
\r
7851 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7853 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7855 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7861 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7865 is = (InputSource *) lParam;
\r
7866 if (is->lineByLine) {
\r
7867 /* Feed in lines one by one */
\r
7868 char *p = is->buf;
\r
7870 while (q < is->next) {
\r
7871 if (*q++ == '\n') {
\r
7872 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7877 /* Move any partial line to the start of the buffer */
\r
7879 while (p < is->next) {
\r
7884 if (is->error != NO_ERROR || is->count == 0) {
\r
7885 /* Notify backend of the error. Note: If there was a partial
\r
7886 line at the end, it is not flushed through. */
\r
7887 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7890 /* Feed in the whole chunk of input at once */
\r
7891 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7892 is->next = is->buf;
\r
7896 /*---------------------------------------------------------------------------*\
\r
7898 * Menu enables. Used when setting various modes.
\r
7900 \*---------------------------------------------------------------------------*/
\r
7908 GreyRevert(Boolean grey)
\r
7909 { // [HGM] vari: for retracting variations in local mode
\r
7910 HMENU hmenu = GetMenu(hwndMain);
\r
7911 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7912 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7916 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7918 while (enab->item > 0) {
\r
7919 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7924 Enables gnuEnables[] = {
\r
7925 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7927 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7929 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7939 // Needed to switch from ncp to GNU mode on Engine Load
\r
7940 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7941 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7942 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7943 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7944 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7945 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7946 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7948 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7949 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7950 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7951 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7952 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7953 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7957 Enables icsEnables[] = {
\r
7958 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7966 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7982 Enables zippyEnables[] = {
\r
7983 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7984 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7985 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7986 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7991 Enables ncpEnables[] = {
\r
7992 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7993 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7994 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8002 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8004 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8017 Enables trainingOnEnables[] = {
\r
8018 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8030 Enables trainingOffEnables[] = {
\r
8031 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8032 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8033 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8034 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8043 /* These modify either ncpEnables or gnuEnables */
\r
8044 Enables cmailEnables[] = {
\r
8045 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8048 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8051 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8055 Enables machineThinkingEnables[] = {
\r
8056 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8057 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8058 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8059 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8060 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8061 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8063 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8065 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8066 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8067 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8068 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8069 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8071 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8075 Enables userThinkingEnables[] = {
\r
8076 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8077 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8078 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8079 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8080 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8081 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8082 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8083 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8084 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8085 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8086 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8087 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8088 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8089 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8090 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8091 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8095 /*---------------------------------------------------------------------------*\
\r
8097 * Front-end interface functions exported by XBoard.
\r
8098 * Functions appear in same order as prototypes in frontend.h.
\r
8100 \*---------------------------------------------------------------------------*/
\r
8102 CheckMark(UINT item, int state)
\r
8104 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8110 static UINT prevChecked = 0;
\r
8111 static int prevPausing = 0;
\r
8114 if (pausing != prevPausing) {
\r
8115 prevPausing = pausing;
\r
8116 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8117 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8118 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8121 switch (gameMode) {
\r
8122 case BeginningOfGame:
\r
8123 if (appData.icsActive)
\r
8124 nowChecked = IDM_IcsClient;
\r
8125 else if (appData.noChessProgram)
\r
8126 nowChecked = IDM_EditGame;
\r
8128 nowChecked = IDM_MachineBlack;
\r
8130 case MachinePlaysBlack:
\r
8131 nowChecked = IDM_MachineBlack;
\r
8133 case MachinePlaysWhite:
\r
8134 nowChecked = IDM_MachineWhite;
\r
8136 case TwoMachinesPlay:
\r
8137 nowChecked = IDM_TwoMachines;
\r
8140 nowChecked = IDM_AnalysisMode;
\r
8143 nowChecked = IDM_AnalyzeFile;
\r
8146 nowChecked = IDM_EditGame;
\r
8148 case PlayFromGameFile:
\r
8149 nowChecked = IDM_LoadGame;
\r
8151 case EditPosition:
\r
8152 nowChecked = IDM_EditPosition;
\r
8155 nowChecked = IDM_Training;
\r
8157 case IcsPlayingWhite:
\r
8158 case IcsPlayingBlack:
\r
8159 case IcsObserving:
\r
8161 nowChecked = IDM_IcsClient;
\r
8168 CheckMark(prevChecked, MF_UNCHECKED);
\r
8169 CheckMark(nowChecked, MF_CHECKED);
\r
8170 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8172 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8173 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8174 MF_BYCOMMAND|MF_ENABLED);
\r
8176 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8177 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8180 prevChecked = nowChecked;
\r
8182 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8183 if (appData.icsActive) {
\r
8184 if (appData.icsEngineAnalyze) {
\r
8185 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8187 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8190 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8196 HMENU hmenu = GetMenu(hwndMain);
\r
8197 SetMenuEnables(hmenu, icsEnables);
\r
8198 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8199 MF_BYCOMMAND|MF_ENABLED);
\r
8201 if (appData.zippyPlay) {
\r
8202 SetMenuEnables(hmenu, zippyEnables);
\r
8203 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8204 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8205 MF_BYCOMMAND|MF_ENABLED);
\r
8213 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8219 HMENU hmenu = GetMenu(hwndMain);
\r
8220 SetMenuEnables(hmenu, ncpEnables);
\r
8221 DrawMenuBar(hwndMain);
\r
8227 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8231 SetTrainingModeOn()
\r
8234 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8235 for (i = 0; i < N_BUTTONS; i++) {
\r
8236 if (buttonDesc[i].hwnd != NULL)
\r
8237 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8242 VOID SetTrainingModeOff()
\r
8245 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8246 for (i = 0; i < N_BUTTONS; i++) {
\r
8247 if (buttonDesc[i].hwnd != NULL)
\r
8248 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8254 SetUserThinkingEnables()
\r
8256 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8260 SetMachineThinkingEnables()
\r
8262 HMENU hMenu = GetMenu(hwndMain);
\r
8263 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8265 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8267 if (gameMode == MachinePlaysBlack) {
\r
8268 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8269 } else if (gameMode == MachinePlaysWhite) {
\r
8270 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8271 } else if (gameMode == TwoMachinesPlay) {
\r
8272 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8278 DisplayTitle(char *str)
\r
8280 char title[MSG_SIZ], *host;
\r
8281 if (str[0] != NULLCHAR) {
\r
8282 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8283 } else if (appData.icsActive) {
\r
8284 if (appData.icsCommPort[0] != NULLCHAR)
\r
8287 host = appData.icsHost;
\r
8288 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8289 } else if (appData.noChessProgram) {
\r
8290 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8292 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8293 strcat(title, ": ");
\r
8294 strcat(title, first.tidy);
\r
8296 SetWindowText(hwndMain, title);
\r
8301 DisplayMessage(char *str1, char *str2)
\r
8305 int remain = MESSAGE_TEXT_MAX - 1;
\r
8308 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8309 messageText[0] = NULLCHAR;
\r
8311 len = strlen(str1);
\r
8312 if (len > remain) len = remain;
\r
8313 strncpy(messageText, str1, len);
\r
8314 messageText[len] = NULLCHAR;
\r
8317 if (*str2 && remain >= 2) {
\r
8319 strcat(messageText, " ");
\r
8322 len = strlen(str2);
\r
8323 if (len > remain) len = remain;
\r
8324 strncat(messageText, str2, len);
\r
8326 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8327 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8329 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8333 hdc = GetDC(hwndMain);
\r
8334 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8335 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8336 &messageRect, messageText, strlen(messageText), NULL);
\r
8337 (void) SelectObject(hdc, oldFont);
\r
8338 (void) ReleaseDC(hwndMain, hdc);
\r
8342 DisplayError(char *str, int error)
\r
8344 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8348 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8350 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8351 NULL, error, LANG_NEUTRAL,
\r
8352 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8354 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8356 ErrorMap *em = errmap;
\r
8357 while (em->err != 0 && em->err != error) em++;
\r
8358 if (em->err != 0) {
\r
8359 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8361 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8366 ErrorPopUp(_("Error"), buf);
\r
8371 DisplayMoveError(char *str)
\r
8373 fromX = fromY = -1;
\r
8374 ClearHighlights();
\r
8375 DrawPosition(FALSE, NULL);
\r
8376 if (appData.popupMoveErrors) {
\r
8377 ErrorPopUp(_("Error"), str);
\r
8379 DisplayMessage(str, "");
\r
8380 moveErrorMessageUp = TRUE;
\r
8385 DisplayFatalError(char *str, int error, int exitStatus)
\r
8387 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8389 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8392 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8393 NULL, error, LANG_NEUTRAL,
\r
8394 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8396 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8398 ErrorMap *em = errmap;
\r
8399 while (em->err != 0 && em->err != error) em++;
\r
8400 if (em->err != 0) {
\r
8401 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8403 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8408 if (appData.debugMode) {
\r
8409 fprintf(debugFP, "%s: %s\n", label, str);
\r
8411 if (appData.popupExitMessage) {
\r
8412 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8413 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8415 ExitEvent(exitStatus);
\r
8420 DisplayInformation(char *str)
\r
8422 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8427 DisplayNote(char *str)
\r
8429 ErrorPopUp(_("Note"), str);
\r
8434 char *title, *question, *replyPrefix;
\r
8439 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8441 static QuestionParams *qp;
\r
8442 char reply[MSG_SIZ];
\r
8445 switch (message) {
\r
8446 case WM_INITDIALOG:
\r
8447 qp = (QuestionParams *) lParam;
\r
8448 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8449 Translate(hDlg, DLG_Question);
\r
8450 SetWindowText(hDlg, qp->title);
\r
8451 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8452 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8456 switch (LOWORD(wParam)) {
\r
8458 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8459 if (*reply) strcat(reply, " ");
\r
8460 len = strlen(reply);
\r
8461 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8462 strcat(reply, "\n");
\r
8463 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8464 EndDialog(hDlg, TRUE);
\r
8465 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8468 EndDialog(hDlg, FALSE);
\r
8479 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8481 QuestionParams qp;
\r
8485 qp.question = question;
\r
8486 qp.replyPrefix = replyPrefix;
\r
8488 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8489 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8490 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8491 FreeProcInstance(lpProc);
\r
8494 /* [AS] Pick FRC position */
\r
8495 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8497 static int * lpIndexFRC;
\r
8503 case WM_INITDIALOG:
\r
8504 lpIndexFRC = (int *) lParam;
\r
8506 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8507 Translate(hDlg, DLG_NewGameFRC);
\r
8509 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8510 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8511 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8512 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8517 switch( LOWORD(wParam) ) {
\r
8519 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8520 EndDialog( hDlg, 0 );
\r
8521 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8524 EndDialog( hDlg, 1 );
\r
8526 case IDC_NFG_Edit:
\r
8527 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8528 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8530 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8533 case IDC_NFG_Random:
\r
8534 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8535 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8548 int index = appData.defaultFrcPosition;
\r
8549 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8551 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8553 if( result == 0 ) {
\r
8554 appData.defaultFrcPosition = index;
\r
8560 /* [AS] Game list options. Refactored by HGM */
\r
8562 HWND gameListOptionsDialog;
\r
8564 // low-level front-end: clear text edit / list widget
\r
8569 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8572 // low-level front-end: clear text edit / list widget
\r
8574 GLT_DeSelectList()
\r
8576 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8579 // low-level front-end: append line to text edit / list widget
\r
8581 GLT_AddToList( char *name )
\r
8584 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8588 // low-level front-end: get line from text edit / list widget
\r
8590 GLT_GetFromList( int index, char *name )
\r
8593 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8599 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8601 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8602 int idx2 = idx1 + delta;
\r
8603 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8605 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8608 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8609 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8610 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8611 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8615 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8619 case WM_INITDIALOG:
\r
8620 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8622 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8623 Translate(hDlg, DLG_GameListOptions);
\r
8625 /* Initialize list */
\r
8626 GLT_TagsToList( lpUserGLT );
\r
8628 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8633 switch( LOWORD(wParam) ) {
\r
8636 EndDialog( hDlg, 0 );
\r
8639 EndDialog( hDlg, 1 );
\r
8642 case IDC_GLT_Default:
\r
8643 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8646 case IDC_GLT_Restore:
\r
8647 GLT_TagsToList( appData.gameListTags );
\r
8651 GLT_MoveSelection( hDlg, -1 );
\r
8654 case IDC_GLT_Down:
\r
8655 GLT_MoveSelection( hDlg, +1 );
\r
8665 int GameListOptions()
\r
8668 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8670 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8672 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8674 if( result == 0 ) {
\r
8675 /* [AS] Memory leak here! */
\r
8676 appData.gameListTags = strdup( lpUserGLT );
\r
8683 DisplayIcsInteractionTitle(char *str)
\r
8685 char consoleTitle[MSG_SIZ];
\r
8687 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8688 SetWindowText(hwndConsole, consoleTitle);
\r
8690 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8691 char buf[MSG_SIZ], *p = buf, *q;
\r
8692 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8694 q = strchr(p, ';');
\r
8696 if(*p) ChatPopUp(p);
\r
8700 SetActiveWindow(hwndMain);
\r
8704 DrawPosition(int fullRedraw, Board board)
\r
8706 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8709 void NotifyFrontendLogin()
\r
8712 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8718 fromX = fromY = -1;
\r
8719 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8720 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8721 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8722 dragInfo.lastpos = dragInfo.pos;
\r
8723 dragInfo.start.x = dragInfo.start.y = -1;
\r
8724 dragInfo.from = dragInfo.start;
\r
8726 DrawPosition(TRUE, NULL);
\r
8733 CommentPopUp(char *title, char *str)
\r
8735 HWND hwnd = GetActiveWindow();
\r
8736 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8738 SetActiveWindow(hwnd);
\r
8742 CommentPopDown(void)
\r
8744 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8745 if (commentDialog) {
\r
8746 ShowWindow(commentDialog, SW_HIDE);
\r
8748 commentUp = FALSE;
\r
8752 EditCommentPopUp(int index, char *title, char *str)
\r
8754 EitherCommentPopUp(index, title, str, TRUE);
\r
8761 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8768 MyPlaySound(&sounds[(int)SoundMove]);
\r
8771 VOID PlayIcsWinSound()
\r
8773 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8776 VOID PlayIcsLossSound()
\r
8778 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8781 VOID PlayIcsDrawSound()
\r
8783 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8786 VOID PlayIcsUnfinishedSound()
\r
8788 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8794 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8800 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8808 consoleEcho = TRUE;
\r
8809 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8810 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8811 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8820 consoleEcho = FALSE;
\r
8821 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8822 /* This works OK: set text and background both to the same color */
\r
8824 cf.crTextColor = COLOR_ECHOOFF;
\r
8825 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8826 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8829 /* No Raw()...? */
\r
8831 void Colorize(ColorClass cc, int continuation)
\r
8833 currentColorClass = cc;
\r
8834 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8835 consoleCF.crTextColor = textAttribs[cc].color;
\r
8836 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8837 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8843 static char buf[MSG_SIZ];
\r
8844 DWORD bufsiz = MSG_SIZ;
\r
8846 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8847 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8849 if (!GetUserName(buf, &bufsiz)) {
\r
8850 /*DisplayError("Error getting user name", GetLastError());*/
\r
8851 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8859 static char buf[MSG_SIZ];
\r
8860 DWORD bufsiz = MSG_SIZ;
\r
8862 if (!GetComputerName(buf, &bufsiz)) {
\r
8863 /*DisplayError("Error getting host name", GetLastError());*/
\r
8864 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8871 ClockTimerRunning()
\r
8873 return clockTimerEvent != 0;
\r
8879 if (clockTimerEvent == 0) return FALSE;
\r
8880 KillTimer(hwndMain, clockTimerEvent);
\r
8881 clockTimerEvent = 0;
\r
8886 StartClockTimer(long millisec)
\r
8888 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8889 (UINT) millisec, NULL);
\r
8893 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8896 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8898 if(appData.noGUI) return;
\r
8899 hdc = GetDC(hwndMain);
\r
8900 if (!IsIconic(hwndMain)) {
\r
8901 DisplayAClock(hdc, timeRemaining, highlight,
\r
8902 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8904 if (highlight && iconCurrent == iconBlack) {
\r
8905 iconCurrent = iconWhite;
\r
8906 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8907 if (IsIconic(hwndMain)) {
\r
8908 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8911 (void) ReleaseDC(hwndMain, hdc);
\r
8913 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8917 DisplayBlackClock(long timeRemaining, int highlight)
\r
8920 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8923 if(appData.noGUI) return;
\r
8924 hdc = GetDC(hwndMain);
\r
8925 if (!IsIconic(hwndMain)) {
\r
8926 DisplayAClock(hdc, timeRemaining, highlight,
\r
8927 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8929 if (highlight && iconCurrent == iconWhite) {
\r
8930 iconCurrent = iconBlack;
\r
8931 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8932 if (IsIconic(hwndMain)) {
\r
8933 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8936 (void) ReleaseDC(hwndMain, hdc);
\r
8938 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8943 LoadGameTimerRunning()
\r
8945 return loadGameTimerEvent != 0;
\r
8949 StopLoadGameTimer()
\r
8951 if (loadGameTimerEvent == 0) return FALSE;
\r
8952 KillTimer(hwndMain, loadGameTimerEvent);
\r
8953 loadGameTimerEvent = 0;
\r
8958 StartLoadGameTimer(long millisec)
\r
8960 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8961 (UINT) millisec, NULL);
\r
8969 char fileTitle[MSG_SIZ];
\r
8971 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8972 f = OpenFileDialog(hwndMain, "a", defName,
\r
8973 appData.oldSaveStyle ? "gam" : "pgn",
\r
8975 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8977 SaveGame(f, 0, "");
\r
8984 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8986 if (delayedTimerEvent != 0) {
\r
8987 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8988 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8990 KillTimer(hwndMain, delayedTimerEvent);
\r
8991 delayedTimerEvent = 0;
\r
8992 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8993 delayedTimerCallback();
\r
8995 delayedTimerCallback = cb;
\r
8996 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8997 (UINT) millisec, NULL);
\r
9000 DelayedEventCallback
\r
9003 if (delayedTimerEvent) {
\r
9004 return delayedTimerCallback;
\r
9011 CancelDelayedEvent()
\r
9013 if (delayedTimerEvent) {
\r
9014 KillTimer(hwndMain, delayedTimerEvent);
\r
9015 delayedTimerEvent = 0;
\r
9019 DWORD GetWin32Priority(int nice)
\r
9020 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9022 REALTIME_PRIORITY_CLASS 0x00000100
\r
9023 HIGH_PRIORITY_CLASS 0x00000080
\r
9024 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9025 NORMAL_PRIORITY_CLASS 0x00000020
\r
9026 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9027 IDLE_PRIORITY_CLASS 0x00000040
\r
9029 if (nice < -15) return 0x00000080;
\r
9030 if (nice < 0) return 0x00008000;
\r
9031 if (nice == 0) return 0x00000020;
\r
9032 if (nice < 15) return 0x00004000;
\r
9033 return 0x00000040;
\r
9036 void RunCommand(char *cmdLine)
\r
9038 /* Now create the child process. */
\r
9039 STARTUPINFO siStartInfo;
\r
9040 PROCESS_INFORMATION piProcInfo;
\r
9042 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9043 siStartInfo.lpReserved = NULL;
\r
9044 siStartInfo.lpDesktop = NULL;
\r
9045 siStartInfo.lpTitle = NULL;
\r
9046 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9047 siStartInfo.cbReserved2 = 0;
\r
9048 siStartInfo.lpReserved2 = NULL;
\r
9049 siStartInfo.hStdInput = NULL;
\r
9050 siStartInfo.hStdOutput = NULL;
\r
9051 siStartInfo.hStdError = NULL;
\r
9053 CreateProcess(NULL,
\r
9054 cmdLine, /* command line */
\r
9055 NULL, /* process security attributes */
\r
9056 NULL, /* primary thread security attrs */
\r
9057 TRUE, /* handles are inherited */
\r
9058 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9059 NULL, /* use parent's environment */
\r
9061 &siStartInfo, /* STARTUPINFO pointer */
\r
9062 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9064 CloseHandle(piProcInfo.hThread);
\r
9067 /* Start a child process running the given program.
\r
9068 The process's standard output can be read from "from", and its
\r
9069 standard input can be written to "to".
\r
9070 Exit with fatal error if anything goes wrong.
\r
9071 Returns an opaque pointer that can be used to destroy the process
\r
9075 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9077 #define BUFSIZE 4096
\r
9079 HANDLE hChildStdinRd, hChildStdinWr,
\r
9080 hChildStdoutRd, hChildStdoutWr;
\r
9081 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9082 SECURITY_ATTRIBUTES saAttr;
\r
9084 PROCESS_INFORMATION piProcInfo;
\r
9085 STARTUPINFO siStartInfo;
\r
9087 char buf[MSG_SIZ];
\r
9090 if (appData.debugMode) {
\r
9091 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9096 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9097 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9098 saAttr.bInheritHandle = TRUE;
\r
9099 saAttr.lpSecurityDescriptor = NULL;
\r
9102 * The steps for redirecting child's STDOUT:
\r
9103 * 1. Create anonymous pipe to be STDOUT for child.
\r
9104 * 2. Create a noninheritable duplicate of read handle,
\r
9105 * and close the inheritable read handle.
\r
9108 /* Create a pipe for the child's STDOUT. */
\r
9109 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9110 return GetLastError();
\r
9113 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9114 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9115 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9116 FALSE, /* not inherited */
\r
9117 DUPLICATE_SAME_ACCESS);
\r
9119 return GetLastError();
\r
9121 CloseHandle(hChildStdoutRd);
\r
9124 * The steps for redirecting child's STDIN:
\r
9125 * 1. Create anonymous pipe to be STDIN for child.
\r
9126 * 2. Create a noninheritable duplicate of write handle,
\r
9127 * and close the inheritable write handle.
\r
9130 /* Create a pipe for the child's STDIN. */
\r
9131 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9132 return GetLastError();
\r
9135 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9136 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9137 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9138 FALSE, /* not inherited */
\r
9139 DUPLICATE_SAME_ACCESS);
\r
9141 return GetLastError();
\r
9143 CloseHandle(hChildStdinWr);
\r
9145 /* Arrange to (1) look in dir for the child .exe file, and
\r
9146 * (2) have dir be the child's working directory. Interpret
\r
9147 * dir relative to the directory WinBoard loaded from. */
\r
9148 GetCurrentDirectory(MSG_SIZ, buf);
\r
9149 SetCurrentDirectory(installDir);
\r
9150 SetCurrentDirectory(dir);
\r
9152 /* Now create the child process. */
\r
9154 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9155 siStartInfo.lpReserved = NULL;
\r
9156 siStartInfo.lpDesktop = NULL;
\r
9157 siStartInfo.lpTitle = NULL;
\r
9158 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9159 siStartInfo.cbReserved2 = 0;
\r
9160 siStartInfo.lpReserved2 = NULL;
\r
9161 siStartInfo.hStdInput = hChildStdinRd;
\r
9162 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9163 siStartInfo.hStdError = hChildStdoutWr;
\r
9165 fSuccess = CreateProcess(NULL,
\r
9166 cmdLine, /* command line */
\r
9167 NULL, /* process security attributes */
\r
9168 NULL, /* primary thread security attrs */
\r
9169 TRUE, /* handles are inherited */
\r
9170 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9171 NULL, /* use parent's environment */
\r
9173 &siStartInfo, /* STARTUPINFO pointer */
\r
9174 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9176 err = GetLastError();
\r
9177 SetCurrentDirectory(buf); /* return to prev directory */
\r
9182 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9183 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9184 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9187 /* Close the handles we don't need in the parent */
\r
9188 CloseHandle(piProcInfo.hThread);
\r
9189 CloseHandle(hChildStdinRd);
\r
9190 CloseHandle(hChildStdoutWr);
\r
9192 /* Prepare return value */
\r
9193 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9194 cp->kind = CPReal;
\r
9195 cp->hProcess = piProcInfo.hProcess;
\r
9196 cp->pid = piProcInfo.dwProcessId;
\r
9197 cp->hFrom = hChildStdoutRdDup;
\r
9198 cp->hTo = hChildStdinWrDup;
\r
9200 *pr = (void *) cp;
\r
9202 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9203 2000 where engines sometimes don't see the initial command(s)
\r
9204 from WinBoard and hang. I don't understand how that can happen,
\r
9205 but the Sleep is harmless, so I've put it in. Others have also
\r
9206 reported what may be the same problem, so hopefully this will fix
\r
9207 it for them too. */
\r
9215 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9217 ChildProc *cp; int result;
\r
9219 cp = (ChildProc *) pr;
\r
9220 if (cp == NULL) return;
\r
9222 switch (cp->kind) {
\r
9224 /* TerminateProcess is considered harmful, so... */
\r
9225 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9226 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9227 /* The following doesn't work because the chess program
\r
9228 doesn't "have the same console" as WinBoard. Maybe
\r
9229 we could arrange for this even though neither WinBoard
\r
9230 nor the chess program uses a console for stdio? */
\r
9231 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9233 /* [AS] Special termination modes for misbehaving programs... */
\r
9234 if( signal & 8 ) {
\r
9235 result = TerminateProcess( cp->hProcess, 0 );
\r
9237 if ( appData.debugMode) {
\r
9238 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9241 else if( signal & 4 ) {
\r
9242 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9244 if( dw != WAIT_OBJECT_0 ) {
\r
9245 result = TerminateProcess( cp->hProcess, 0 );
\r
9247 if ( appData.debugMode) {
\r
9248 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9254 CloseHandle(cp->hProcess);
\r
9258 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9262 closesocket(cp->sock);
\r
9267 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9268 closesocket(cp->sock);
\r
9269 closesocket(cp->sock2);
\r
9277 InterruptChildProcess(ProcRef pr)
\r
9281 cp = (ChildProc *) pr;
\r
9282 if (cp == NULL) return;
\r
9283 switch (cp->kind) {
\r
9285 /* The following doesn't work because the chess program
\r
9286 doesn't "have the same console" as WinBoard. Maybe
\r
9287 we could arrange for this even though neither WinBoard
\r
9288 nor the chess program uses a console for stdio */
\r
9289 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9294 /* Can't interrupt */
\r
9298 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9305 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9307 char cmdLine[MSG_SIZ];
\r
9309 if (port[0] == NULLCHAR) {
\r
9310 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9312 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9314 return StartChildProcess(cmdLine, "", pr);
\r
9318 /* Code to open TCP sockets */
\r
9321 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9327 struct sockaddr_in sa, mysa;
\r
9328 struct hostent FAR *hp;
\r
9329 unsigned short uport;
\r
9330 WORD wVersionRequested;
\r
9333 /* Initialize socket DLL */
\r
9334 wVersionRequested = MAKEWORD(1, 1);
\r
9335 err = WSAStartup(wVersionRequested, &wsaData);
\r
9336 if (err != 0) return err;
\r
9339 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9340 err = WSAGetLastError();
\r
9345 /* Bind local address using (mostly) don't-care values.
\r
9347 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9348 mysa.sin_family = AF_INET;
\r
9349 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9350 uport = (unsigned short) 0;
\r
9351 mysa.sin_port = htons(uport);
\r
9352 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9353 == SOCKET_ERROR) {
\r
9354 err = WSAGetLastError();
\r
9359 /* Resolve remote host name */
\r
9360 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9361 if (!(hp = gethostbyname(host))) {
\r
9362 unsigned int b0, b1, b2, b3;
\r
9364 err = WSAGetLastError();
\r
9366 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9367 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9368 hp->h_addrtype = AF_INET;
\r
9370 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9371 hp->h_addr_list[0] = (char *) malloc(4);
\r
9372 hp->h_addr_list[0][0] = (char) b0;
\r
9373 hp->h_addr_list[0][1] = (char) b1;
\r
9374 hp->h_addr_list[0][2] = (char) b2;
\r
9375 hp->h_addr_list[0][3] = (char) b3;
\r
9381 sa.sin_family = hp->h_addrtype;
\r
9382 uport = (unsigned short) atoi(port);
\r
9383 sa.sin_port = htons(uport);
\r
9384 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9386 /* Make connection */
\r
9387 if (connect(s, (struct sockaddr *) &sa,
\r
9388 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9389 err = WSAGetLastError();
\r
9394 /* Prepare return value */
\r
9395 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9396 cp->kind = CPSock;
\r
9398 *pr = (ProcRef *) cp;
\r
9404 OpenCommPort(char *name, ProcRef *pr)
\r
9409 char fullname[MSG_SIZ];
\r
9411 if (*name != '\\')
\r
9412 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9414 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9416 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9417 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9418 if (h == (HANDLE) -1) {
\r
9419 return GetLastError();
\r
9423 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9425 /* Accumulate characters until a 100ms pause, then parse */
\r
9426 ct.ReadIntervalTimeout = 100;
\r
9427 ct.ReadTotalTimeoutMultiplier = 0;
\r
9428 ct.ReadTotalTimeoutConstant = 0;
\r
9429 ct.WriteTotalTimeoutMultiplier = 0;
\r
9430 ct.WriteTotalTimeoutConstant = 0;
\r
9431 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9433 /* Prepare return value */
\r
9434 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9435 cp->kind = CPComm;
\r
9438 *pr = (ProcRef *) cp;
\r
9444 OpenLoopback(ProcRef *pr)
\r
9446 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9452 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9457 struct sockaddr_in sa, mysa;
\r
9458 struct hostent FAR *hp;
\r
9459 unsigned short uport;
\r
9460 WORD wVersionRequested;
\r
9463 char stderrPortStr[MSG_SIZ];
\r
9465 /* Initialize socket DLL */
\r
9466 wVersionRequested = MAKEWORD(1, 1);
\r
9467 err = WSAStartup(wVersionRequested, &wsaData);
\r
9468 if (err != 0) return err;
\r
9470 /* Resolve remote host name */
\r
9471 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9472 if (!(hp = gethostbyname(host))) {
\r
9473 unsigned int b0, b1, b2, b3;
\r
9475 err = WSAGetLastError();
\r
9477 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9478 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9479 hp->h_addrtype = AF_INET;
\r
9481 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9482 hp->h_addr_list[0] = (char *) malloc(4);
\r
9483 hp->h_addr_list[0][0] = (char) b0;
\r
9484 hp->h_addr_list[0][1] = (char) b1;
\r
9485 hp->h_addr_list[0][2] = (char) b2;
\r
9486 hp->h_addr_list[0][3] = (char) b3;
\r
9492 sa.sin_family = hp->h_addrtype;
\r
9493 uport = (unsigned short) 514;
\r
9494 sa.sin_port = htons(uport);
\r
9495 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9497 /* Bind local socket to unused "privileged" port address
\r
9499 s = INVALID_SOCKET;
\r
9500 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9501 mysa.sin_family = AF_INET;
\r
9502 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9503 for (fromPort = 1023;; fromPort--) {
\r
9504 if (fromPort < 0) {
\r
9506 return WSAEADDRINUSE;
\r
9508 if (s == INVALID_SOCKET) {
\r
9509 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9510 err = WSAGetLastError();
\r
9515 uport = (unsigned short) fromPort;
\r
9516 mysa.sin_port = htons(uport);
\r
9517 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9518 == SOCKET_ERROR) {
\r
9519 err = WSAGetLastError();
\r
9520 if (err == WSAEADDRINUSE) continue;
\r
9524 if (connect(s, (struct sockaddr *) &sa,
\r
9525 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9526 err = WSAGetLastError();
\r
9527 if (err == WSAEADDRINUSE) {
\r
9538 /* Bind stderr local socket to unused "privileged" port address
\r
9540 s2 = INVALID_SOCKET;
\r
9541 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9542 mysa.sin_family = AF_INET;
\r
9543 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9544 for (fromPort = 1023;; fromPort--) {
\r
9545 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9546 if (fromPort < 0) {
\r
9547 (void) closesocket(s);
\r
9549 return WSAEADDRINUSE;
\r
9551 if (s2 == INVALID_SOCKET) {
\r
9552 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9553 err = WSAGetLastError();
\r
9559 uport = (unsigned short) fromPort;
\r
9560 mysa.sin_port = htons(uport);
\r
9561 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9562 == SOCKET_ERROR) {
\r
9563 err = WSAGetLastError();
\r
9564 if (err == WSAEADDRINUSE) continue;
\r
9565 (void) closesocket(s);
\r
9569 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9570 err = WSAGetLastError();
\r
9571 if (err == WSAEADDRINUSE) {
\r
9573 s2 = INVALID_SOCKET;
\r
9576 (void) closesocket(s);
\r
9577 (void) closesocket(s2);
\r
9583 prevStderrPort = fromPort; // remember port used
\r
9584 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9586 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9587 err = WSAGetLastError();
\r
9588 (void) closesocket(s);
\r
9589 (void) closesocket(s2);
\r
9594 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9595 err = WSAGetLastError();
\r
9596 (void) closesocket(s);
\r
9597 (void) closesocket(s2);
\r
9601 if (*user == NULLCHAR) user = UserName();
\r
9602 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9603 err = WSAGetLastError();
\r
9604 (void) closesocket(s);
\r
9605 (void) closesocket(s2);
\r
9609 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9610 err = WSAGetLastError();
\r
9611 (void) closesocket(s);
\r
9612 (void) closesocket(s2);
\r
9617 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9618 err = WSAGetLastError();
\r
9619 (void) closesocket(s);
\r
9620 (void) closesocket(s2);
\r
9624 (void) closesocket(s2); /* Stop listening */
\r
9626 /* Prepare return value */
\r
9627 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9628 cp->kind = CPRcmd;
\r
9631 *pr = (ProcRef *) cp;
\r
9638 AddInputSource(ProcRef pr, int lineByLine,
\r
9639 InputCallback func, VOIDSTAR closure)
\r
9641 InputSource *is, *is2 = NULL;
\r
9642 ChildProc *cp = (ChildProc *) pr;
\r
9644 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9645 is->lineByLine = lineByLine;
\r
9647 is->closure = closure;
\r
9648 is->second = NULL;
\r
9649 is->next = is->buf;
\r
9650 if (pr == NoProc) {
\r
9651 is->kind = CPReal;
\r
9652 consoleInputSource = is;
\r
9654 is->kind = cp->kind;
\r
9656 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9657 we create all threads suspended so that the is->hThread variable can be
\r
9658 safely assigned, then let the threads start with ResumeThread.
\r
9660 switch (cp->kind) {
\r
9662 is->hFile = cp->hFrom;
\r
9663 cp->hFrom = NULL; /* now owned by InputThread */
\r
9665 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9666 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9670 is->hFile = cp->hFrom;
\r
9671 cp->hFrom = NULL; /* now owned by InputThread */
\r
9673 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9674 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9678 is->sock = cp->sock;
\r
9680 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9681 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9685 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9687 is->sock = cp->sock;
\r
9689 is2->sock = cp->sock2;
\r
9690 is2->second = is2;
\r
9692 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9693 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9695 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9696 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9700 if( is->hThread != NULL ) {
\r
9701 ResumeThread( is->hThread );
\r
9704 if( is2 != NULL && is2->hThread != NULL ) {
\r
9705 ResumeThread( is2->hThread );
\r
9709 return (InputSourceRef) is;
\r
9713 RemoveInputSource(InputSourceRef isr)
\r
9717 is = (InputSource *) isr;
\r
9718 is->hThread = NULL; /* tell thread to stop */
\r
9719 CloseHandle(is->hThread);
\r
9720 if (is->second != NULL) {
\r
9721 is->second->hThread = NULL;
\r
9722 CloseHandle(is->second->hThread);
\r
9726 int no_wrap(char *message, int count)
\r
9728 ConsoleOutput(message, count, FALSE);
\r
9733 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9736 int outCount = SOCKET_ERROR;
\r
9737 ChildProc *cp = (ChildProc *) pr;
\r
9738 static OVERLAPPED ovl;
\r
9739 static int line = 0;
\r
9743 if (appData.noJoin || !appData.useInternalWrap)
\r
9744 return no_wrap(message, count);
\r
9747 int width = get_term_width();
\r
9748 int len = wrap(NULL, message, count, width, &line);
\r
9749 char *msg = malloc(len);
\r
9753 return no_wrap(message, count);
\r
9756 dbgchk = wrap(msg, message, count, width, &line);
\r
9757 if (dbgchk != len && appData.debugMode)
\r
9758 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9759 ConsoleOutput(msg, len, FALSE);
\r
9766 if (ovl.hEvent == NULL) {
\r
9767 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9769 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9771 switch (cp->kind) {
\r
9774 outCount = send(cp->sock, message, count, 0);
\r
9775 if (outCount == SOCKET_ERROR) {
\r
9776 *outError = WSAGetLastError();
\r
9778 *outError = NO_ERROR;
\r
9783 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9784 &dOutCount, NULL)) {
\r
9785 *outError = NO_ERROR;
\r
9786 outCount = (int) dOutCount;
\r
9788 *outError = GetLastError();
\r
9793 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9794 &dOutCount, &ovl);
\r
9795 if (*outError == NO_ERROR) {
\r
9796 outCount = (int) dOutCount;
\r
9806 if(n != 0) Sleep(n);
\r
9810 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9813 /* Ignore delay, not implemented for WinBoard */
\r
9814 return OutputToProcess(pr, message, count, outError);
\r
9819 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9820 char *buf, int count, int error)
\r
9822 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9825 /* see wgamelist.c for Game List functions */
\r
9826 /* see wedittags.c for Edit Tags functions */
\r
9833 char buf[MSG_SIZ];
\r
9836 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9837 f = fopen(buf, "r");
\r
9839 ProcessICSInitScript(f);
\r
9849 StartAnalysisClock()
\r
9851 if (analysisTimerEvent) return;
\r
9852 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9853 (UINT) 2000, NULL);
\r
9857 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9859 highlightInfo.sq[0].x = fromX;
\r
9860 highlightInfo.sq[0].y = fromY;
\r
9861 highlightInfo.sq[1].x = toX;
\r
9862 highlightInfo.sq[1].y = toY;
\r
9868 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9869 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9873 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9875 premoveHighlightInfo.sq[0].x = fromX;
\r
9876 premoveHighlightInfo.sq[0].y = fromY;
\r
9877 premoveHighlightInfo.sq[1].x = toX;
\r
9878 premoveHighlightInfo.sq[1].y = toY;
\r
9882 ClearPremoveHighlights()
\r
9884 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9885 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9889 ShutDownFrontEnd()
\r
9891 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9892 DeleteClipboardTempFiles();
\r
9898 if (IsIconic(hwndMain))
\r
9899 ShowWindow(hwndMain, SW_RESTORE);
\r
9901 SetActiveWindow(hwndMain);
\r
9905 * Prototypes for animation support routines
\r
9907 static void ScreenSquare(int column, int row, POINT * pt);
\r
9908 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9909 POINT frames[], int * nFrames);
\r
9915 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9916 { // [HGM] atomic: animate blast wave
\r
9919 explodeInfo.fromX = fromX;
\r
9920 explodeInfo.fromY = fromY;
\r
9921 explodeInfo.toX = toX;
\r
9922 explodeInfo.toY = toY;
\r
9923 for(i=1; i<4*kFactor; i++) {
\r
9924 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9925 DrawPosition(FALSE, board);
\r
9926 Sleep(appData.animSpeed);
\r
9928 explodeInfo.radius = 0;
\r
9929 DrawPosition(TRUE, board);
\r
9933 AnimateMove(board, fromX, fromY, toX, toY)
\r
9940 ChessSquare piece;
\r
9941 int x = toX, y = toY;
\r
9942 POINT start, finish, mid;
\r
9943 POINT frames[kFactor * 2 + 1];
\r
9946 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9948 if (!appData.animate) return;
\r
9949 if (doingSizing) return;
\r
9950 if (fromY < 0 || fromX < 0) return;
\r
9951 piece = board[fromY][fromX];
\r
9952 if (piece >= EmptySquare) return;
\r
9954 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9958 ScreenSquare(fromX, fromY, &start);
\r
9959 ScreenSquare(toX, toY, &finish);
\r
9961 /* All moves except knight jumps move in straight line */
\r
9962 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9963 mid.x = start.x + (finish.x - start.x) / 2;
\r
9964 mid.y = start.y + (finish.y - start.y) / 2;
\r
9966 /* Knight: make straight movement then diagonal */
\r
9967 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9968 mid.x = start.x + (finish.x - start.x) / 2;
\r
9972 mid.y = start.y + (finish.y - start.y) / 2;
\r
9976 /* Don't use as many frames for very short moves */
\r
9977 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9978 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9980 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9982 animInfo.from.x = fromX;
\r
9983 animInfo.from.y = fromY;
\r
9984 animInfo.to.x = toX;
\r
9985 animInfo.to.y = toY;
\r
9986 animInfo.lastpos = start;
\r
9987 animInfo.piece = piece;
\r
9988 for (n = 0; n < nFrames; n++) {
\r
9989 animInfo.pos = frames[n];
\r
9990 DrawPosition(FALSE, NULL);
\r
9991 animInfo.lastpos = animInfo.pos;
\r
9992 Sleep(appData.animSpeed);
\r
9994 animInfo.pos = finish;
\r
9995 DrawPosition(FALSE, NULL);
\r
9997 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
9999 animInfo.piece = EmptySquare;
\r
10000 Explode(board, fromX, fromY, toX, toY);
\r
10003 /* Convert board position to corner of screen rect and color */
\r
10006 ScreenSquare(column, row, pt)
\r
10007 int column; int row; POINT * pt;
\r
10010 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10011 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10013 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10014 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10018 /* Generate a series of frame coords from start->mid->finish.
\r
10019 The movement rate doubles until the half way point is
\r
10020 reached, then halves back down to the final destination,
\r
10021 which gives a nice slow in/out effect. The algorithmn
\r
10022 may seem to generate too many intermediates for short
\r
10023 moves, but remember that the purpose is to attract the
\r
10024 viewers attention to the piece about to be moved and
\r
10025 then to where it ends up. Too few frames would be less
\r
10029 Tween(start, mid, finish, factor, frames, nFrames)
\r
10030 POINT * start; POINT * mid;
\r
10031 POINT * finish; int factor;
\r
10032 POINT frames[]; int * nFrames;
\r
10034 int n, fraction = 1, count = 0;
\r
10036 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10037 for (n = 0; n < factor; n++)
\r
10039 for (n = 0; n < factor; n++) {
\r
10040 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10041 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10043 fraction = fraction / 2;
\r
10047 frames[count] = *mid;
\r
10050 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10052 for (n = 0; n < factor; n++) {
\r
10053 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10054 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10056 fraction = fraction * 2;
\r
10058 *nFrames = count;
\r
10062 SettingsPopUp(ChessProgramState *cps)
\r
10063 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10064 EngineOptionsPopup(savedHwnd, cps);
\r
10067 int flock(int fid, int code)
\r
10069 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10071 ov.hEvent = NULL;
\r
10073 ov.OffsetHigh = 0;
\r
10075 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10077 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10078 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10079 default: return -1;
\r
10088 static char col[8][20];
\r
10089 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10091 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10096 ActivateTheme (int new)
\r
10097 { // Redo initialization of features depending on options that can occur in themes
\r
10099 if(new) InitDrawingColors();
\r
10100 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10101 InitDrawingSizes(boardSize, 0);
\r
10102 InvalidateRect(hwndMain, NULL, TRUE);
\r