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 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize, border;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
191 ColorClass currentColorClass;
\r
193 static HWND savedHwnd;
\r
194 HWND hCommPort = NULL; /* currently open comm port */
\r
195 static HWND hwndPause; /* pause button */
\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
198 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
199 explodeBrush, /* [HGM] atomic */
\r
200 markerBrush, /* [HGM] markers */
\r
201 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
204 static HPEN gridPen = NULL;
\r
205 static HPEN highlightPen = NULL;
\r
206 static HPEN premovePen = NULL;
\r
207 static NPLOGPALETTE pLogPal;
\r
208 static BOOL paletteChanged = FALSE;
\r
209 static HICON iconWhite, iconBlack, iconCurrent;
\r
210 static int doingSizing = FALSE;
\r
211 static int lastSizing = 0;
\r
212 static int prevStderrPort;
\r
213 static HBITMAP userLogo;
\r
215 static HBITMAP liteBackTexture = NULL;
\r
216 static HBITMAP darkBackTexture = NULL;
\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int backTextureSquareSize = 0;
\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
222 #if __GNUC__ && !defined(_winmajor)
\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
228 #if defined(_winmajor)
\r
229 #define oldDialog (_winmajor < 4)
\r
231 #define oldDialog 0
\r
235 #define INTERNATIONAL
\r
237 #ifdef INTERNATIONAL
\r
238 # define _(s) T_(s)
\r
244 # define Translate(x, y)
\r
245 # define LoadLanguageFile(s)
\r
248 #ifdef INTERNATIONAL
\r
250 Boolean barbaric; // flag indicating if translation is needed
\r
252 // list of item numbers used in each dialog (used to alter language at run time)
\r
254 #define ABOUTBOX -1 /* not sure why these are needed */
\r
255 #define ABOUTBOX2 -1
\r
257 int dialogItems[][42] = {
\r
258 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
259 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
260 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
261 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
262 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
263 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
264 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
265 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
266 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
267 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
268 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
269 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
270 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
271 { ABOUTBOX2, IDC_ChessBoard },
\r
272 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
273 OPT_GameListClose, IDC_GameListDoFilter },
\r
274 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
275 { DLG_Error, IDOK },
\r
276 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
277 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
278 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
279 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
280 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
281 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
282 { DLG_IndexNumber, IDC_Index },
\r
283 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
284 { DLG_TypeInName, IDOK, IDCANCEL },
\r
285 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
286 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
287 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
288 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
289 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
290 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
291 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
292 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
293 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
294 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
295 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
296 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
297 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
298 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
299 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
300 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
301 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
302 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
303 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
304 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
305 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
306 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
307 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
308 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
309 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
310 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
311 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
312 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
313 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
314 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
315 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
316 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
317 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
318 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
319 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
320 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
321 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
322 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
323 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
324 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
325 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
326 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
327 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
328 { DLG_MoveHistory },
\r
329 { DLG_EvalGraph },
\r
330 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
331 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
332 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
333 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
334 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
335 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
336 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
337 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
338 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
342 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
343 static int lastChecked;
\r
344 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
345 extern int tinyLayout;
\r
346 extern char * menuBarText[][10];
\r
349 LoadLanguageFile(char *name)
\r
350 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
352 int i=0, j=0, n=0, k;
\r
355 if(!name || name[0] == NULLCHAR) return;
\r
356 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
357 appData.language = oldLanguage;
\r
358 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
359 if((f = fopen(buf, "r")) == NULL) return;
\r
360 while((k = fgetc(f)) != EOF) {
\r
361 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
362 languageBuf[i] = k;
\r
364 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
366 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
367 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
368 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
369 english[j] = languageBuf + n + 1; *p = 0;
\r
370 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
371 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
376 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
378 case 'n': k = '\n'; break;
\r
379 case 'r': k = '\r'; break;
\r
380 case 't': k = '\t'; break;
\r
382 languageBuf[--i] = k;
\r
387 barbaric = (j != 0);
\r
388 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
393 { // return the translation of the given string
\r
394 // efficiency can be improved a lot...
\r
396 static char buf[MSG_SIZ];
\r
397 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
398 if(!barbaric) return s;
\r
399 if(!s) return ""; // sanity
\r
400 while(english[i]) {
\r
401 if(!strcmp(s, english[i])) return foreign[i];
\r
402 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
403 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
412 Translate(HWND hDlg, int dialogID)
\r
413 { // translate all text items in the given dialog
\r
415 char buf[MSG_SIZ], *s;
\r
416 if(!barbaric) return;
\r
417 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
418 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
419 GetWindowText( hDlg, buf, MSG_SIZ );
\r
421 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
422 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
423 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
424 if(strlen(buf) == 0) continue;
\r
426 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
431 TranslateOneMenu(int i, HMENU subMenu)
\r
434 static MENUITEMINFO info;
\r
436 info.cbSize = sizeof(MENUITEMINFO);
\r
437 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
438 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
440 info.dwTypeData = buf;
\r
441 info.cch = sizeof(buf);
\r
442 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
444 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
445 else menuText[i][j] = strdup(buf); // remember original on first change
\r
447 if(buf[0] == NULLCHAR) continue;
\r
448 info.dwTypeData = T_(buf);
\r
449 info.cch = strlen(buf)+1;
\r
450 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
456 TranslateMenus(int addLanguage)
\r
459 WIN32_FIND_DATA fileData;
\r
461 #define IDM_English 1970
\r
463 HMENU mainMenu = GetMenu(hwndMain);
\r
464 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
465 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
466 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
467 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
468 TranslateOneMenu(i, subMenu);
\r
470 DrawMenuBar(hwndMain);
\r
473 if(!addLanguage) return;
\r
474 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
475 HMENU mainMenu = GetMenu(hwndMain);
\r
476 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
477 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
478 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
479 i = 0; lastChecked = IDM_English;
\r
481 char *p, *q = fileData.cFileName;
\r
482 int checkFlag = MF_UNCHECKED;
\r
483 languageFile[i] = strdup(q);
\r
484 if(barbaric && !strcmp(oldLanguage, q)) {
\r
485 checkFlag = MF_CHECKED;
\r
486 lastChecked = IDM_English + i + 1;
\r
487 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
489 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
490 p = strstr(fileData.cFileName, ".lng");
\r
492 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
493 } while(FindNextFile(hFind, &fileData));
\r
500 #define IDM_RecentEngines 3000
\r
503 RecentEngineMenu (char *s)
\r
505 if(appData.icsActive) return;
\r
506 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
507 HMENU mainMenu = GetMenu(hwndMain);
\r
508 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
509 int i=IDM_RecentEngines;
\r
510 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
511 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
513 char *p = strchr(s, '\n');
\r
514 if(p == NULL) return; // malformed!
\r
516 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
530 int cliWidth, cliHeight;
\r
533 SizeInfo sizeInfo[] =
\r
535 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
536 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
537 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
538 { "petite", 33, 1, 1, 1, 0, 0 },
\r
539 { "slim", 37, 2, 1, 0, 0, 0 },
\r
540 { "small", 40, 2, 1, 0, 0, 0 },
\r
541 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
542 { "middling", 49, 2, 0, 0, 0, 0 },
\r
543 { "average", 54, 2, 0, 0, 0, 0 },
\r
544 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
545 { "medium", 64, 3, 0, 0, 0, 0 },
\r
546 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
547 { "large", 80, 3, 0, 0, 0, 0 },
\r
548 { "big", 87, 3, 0, 0, 0, 0 },
\r
549 { "huge", 95, 3, 0, 0, 0, 0 },
\r
550 { "giant", 108, 3, 0, 0, 0, 0 },
\r
551 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
552 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
553 { NULL, 0, 0, 0, 0, 0, 0 }
\r
556 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
557 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
579 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
588 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
589 #define N_BUTTONS 5
\r
591 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
593 {"<<", IDM_ToStart, NULL, NULL},
\r
594 {"<", IDM_Backward, NULL, NULL},
\r
595 {"P", IDM_Pause, NULL, NULL},
\r
596 {">", IDM_Forward, NULL, NULL},
\r
597 {">>", IDM_ToEnd, NULL, NULL},
\r
600 int tinyLayout = 0, smallLayout = 0;
\r
601 #define MENU_BAR_ITEMS 9
\r
602 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
603 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
604 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
608 MySound sounds[(int)NSoundClasses];
\r
609 MyTextAttribs textAttribs[(int)NColorClasses];
\r
611 MyColorizeAttribs colorizeAttribs[] = {
\r
612 { (COLORREF)0, 0, N_("Shout Text") },
\r
613 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
614 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
615 { (COLORREF)0, 0, N_("Channel Text") },
\r
616 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
617 { (COLORREF)0, 0, N_("Tell Text") },
\r
618 { (COLORREF)0, 0, N_("Challenge Text") },
\r
619 { (COLORREF)0, 0, N_("Request Text") },
\r
620 { (COLORREF)0, 0, N_("Seek Text") },
\r
621 { (COLORREF)0, 0, N_("Normal Text") },
\r
622 { (COLORREF)0, 0, N_("None") }
\r
627 static char *commentTitle;
\r
628 static char *commentText;
\r
629 static int commentIndex;
\r
630 static Boolean editComment = FALSE;
\r
633 char errorTitle[MSG_SIZ];
\r
634 char errorMessage[2*MSG_SIZ];
\r
635 HWND errorDialog = NULL;
\r
636 BOOLEAN moveErrorMessageUp = FALSE;
\r
637 BOOLEAN consoleEcho = TRUE;
\r
638 CHARFORMAT consoleCF;
\r
639 COLORREF consoleBackgroundColor;
\r
641 char *programVersion;
\r
647 typedef int CPKind;
\r
656 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
659 #define INPUT_SOURCE_BUF_SIZE 4096
\r
661 typedef struct _InputSource {
\r
668 char buf[INPUT_SOURCE_BUF_SIZE];
\r
672 InputCallback func;
\r
673 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
677 InputSource *consoleInputSource;
\r
682 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
683 VOID ConsoleCreate();
\r
685 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
686 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
687 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
688 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
690 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
691 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
692 void ParseIcsTextMenu(char *icsTextMenuString);
\r
693 VOID PopUpNameDialog(char firstchar);
\r
694 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
698 int GameListOptions();
\r
700 int dummy; // [HGM] for obsolete args
\r
702 HWND hwndMain = NULL; /* root window*/
\r
703 HWND hwndConsole = NULL;
\r
704 HWND commentDialog = NULL;
\r
705 HWND moveHistoryDialog = NULL;
\r
706 HWND evalGraphDialog = NULL;
\r
707 HWND engineOutputDialog = NULL;
\r
708 HWND gameListDialog = NULL;
\r
709 HWND editTagsDialog = NULL;
\r
711 int commentUp = FALSE;
\r
713 WindowPlacement wpMain;
\r
714 WindowPlacement wpConsole;
\r
715 WindowPlacement wpComment;
\r
716 WindowPlacement wpMoveHistory;
\r
717 WindowPlacement wpEvalGraph;
\r
718 WindowPlacement wpEngineOutput;
\r
719 WindowPlacement wpGameList;
\r
720 WindowPlacement wpTags;
\r
722 VOID EngineOptionsPopup(); // [HGM] settings
\r
724 VOID GothicPopUp(char *title, VariantClass variant);
\r
726 * Setting "frozen" should disable all user input other than deleting
\r
727 * the window. We do this while engines are initializing themselves.
\r
729 static int frozen = 0;
\r
730 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
736 if (frozen) return;
\r
738 hmenu = GetMenu(hwndMain);
\r
739 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
740 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
742 DrawMenuBar(hwndMain);
\r
745 /* Undo a FreezeUI */
\r
751 if (!frozen) return;
\r
753 hmenu = GetMenu(hwndMain);
\r
754 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
755 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
757 DrawMenuBar(hwndMain);
\r
760 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
762 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
768 #define JAWS_ALT_INTERCEPT
\r
769 #define JAWS_KBUP_NAVIGATION
\r
770 #define JAWS_KBDOWN_NAVIGATION
\r
771 #define JAWS_MENU_ITEMS
\r
772 #define JAWS_SILENCE
\r
773 #define JAWS_REPLAY
\r
775 #define JAWS_COPYRIGHT
\r
776 #define JAWS_DELETE(X) X
\r
777 #define SAYMACHINEMOVE()
\r
781 /*---------------------------------------------------------------------------*\
\r
785 \*---------------------------------------------------------------------------*/
\r
788 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
789 LPSTR lpCmdLine, int nCmdShow)
\r
792 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
793 // INITCOMMONCONTROLSEX ex;
\r
797 LoadLibrary("RICHED32.DLL");
\r
798 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
800 if (!InitApplication(hInstance)) {
\r
803 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
810 // InitCommonControlsEx(&ex);
\r
811 InitCommonControls();
\r
813 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
814 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
815 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
817 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
819 while (GetMessage(&msg, /* message structure */
\r
820 NULL, /* handle of window receiving the message */
\r
821 0, /* lowest message to examine */
\r
822 0)) /* highest message to examine */
\r
825 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
826 // [HGM] navigate: switch between all windows with tab
\r
827 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
828 int i, currentElement = 0;
\r
830 // first determine what element of the chain we come from (if any)
\r
831 if(appData.icsActive) {
\r
832 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
833 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
835 if(engineOutputDialog && EngineOutputIsUp()) {
\r
836 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
837 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
839 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
840 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
842 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
843 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
844 if(msg.hwnd == e1) currentElement = 2; else
\r
845 if(msg.hwnd == e2) currentElement = 3; else
\r
846 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
847 if(msg.hwnd == mh) currentElement = 4; else
\r
848 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
849 if(msg.hwnd == hText) currentElement = 5; else
\r
850 if(msg.hwnd == hInput) currentElement = 6; else
\r
851 for (i = 0; i < N_BUTTONS; i++) {
\r
852 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
855 // determine where to go to
\r
856 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
858 currentElement = (currentElement + direction) % 7;
\r
859 switch(currentElement) {
\r
861 h = hwndMain; break; // passing this case always makes the loop exit
\r
863 h = buttonDesc[0].hwnd; break; // could be NULL
\r
865 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
868 if(!EngineOutputIsUp()) continue;
\r
871 if(!MoveHistoryIsUp()) continue;
\r
873 // case 6: // input to eval graph does not seem to get here!
\r
874 // if(!EvalGraphIsUp()) continue;
\r
875 // h = evalGraphDialog; break;
\r
877 if(!appData.icsActive) continue;
\r
881 if(!appData.icsActive) continue;
\r
887 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
888 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
891 continue; // this message now has been processed
\r
895 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
896 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
897 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
898 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
899 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
900 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
901 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
902 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
903 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
904 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
905 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
906 for(i=0; i<MAX_CHAT; i++)
\r
907 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
910 if(done) continue; // [HGM] chat: end patch
\r
911 TranslateMessage(&msg); /* Translates virtual key codes */
\r
912 DispatchMessage(&msg); /* Dispatches message to window */
\r
917 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
920 /*---------------------------------------------------------------------------*\
\r
922 * Initialization functions
\r
924 \*---------------------------------------------------------------------------*/
\r
928 { // update user logo if necessary
\r
929 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
931 if(appData.autoLogo) {
\r
932 curName = UserName();
\r
933 if(strcmp(curName, oldUserName)) {
\r
934 GetCurrentDirectory(MSG_SIZ, dir);
\r
935 SetCurrentDirectory(installDir);
\r
936 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
937 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
938 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
939 if(userLogo == NULL)
\r
940 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
941 SetCurrentDirectory(dir); /* return to prev directory */
\r
947 InitApplication(HINSTANCE hInstance)
\r
951 /* Fill in window class structure with parameters that describe the */
\r
954 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
955 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
956 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
957 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
958 wc.hInstance = hInstance; /* Owner of this class */
\r
959 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
960 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
961 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
962 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
963 wc.lpszClassName = szAppName; /* Name to register as */
\r
965 /* Register the window class and return success/failure code. */
\r
966 if (!RegisterClass(&wc)) return FALSE;
\r
968 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
969 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
971 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
972 wc.hInstance = hInstance;
\r
973 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
974 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
975 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
976 wc.lpszMenuName = NULL;
\r
977 wc.lpszClassName = szConsoleName;
\r
979 if (!RegisterClass(&wc)) return FALSE;
\r
984 /* Set by InitInstance, used by EnsureOnScreen */
\r
985 int screenHeight, screenWidth;
\r
986 RECT screenGeometry;
\r
989 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
991 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
992 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
993 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
994 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
995 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
996 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1000 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1002 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1003 GetCurrentDirectory(MSG_SIZ, dir);
\r
1004 SetCurrentDirectory(installDir);
\r
1005 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1006 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1008 if (cps->programLogo == NULL && appData.debugMode) {
\r
1009 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1011 } else if(appData.autoLogo) {
\r
1012 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1013 char *opponent = "";
\r
1014 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1015 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1016 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1017 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1018 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1019 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1022 if(appData.directory[n] && appData.directory[n][0]) {
\r
1023 SetCurrentDirectory(appData.directory[n]);
\r
1024 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1027 SetCurrentDirectory(dir); /* return to prev directory */
\r
1033 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1034 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1036 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1037 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1038 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1039 liteBackTextureMode = appData.liteBackTextureMode;
\r
1041 if (liteBackTexture == NULL && appData.debugMode) {
\r
1042 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1046 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1047 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1048 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1049 darkBackTextureMode = appData.darkBackTextureMode;
\r
1051 if (darkBackTexture == NULL && appData.debugMode) {
\r
1052 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1057 #ifndef SM_CXVIRTUALSCREEN
\r
1058 #define SM_CXVIRTUALSCREEN 78
\r
1060 #ifndef SM_CYVIRTUALSCREEN
\r
1061 #define SM_CYVIRTUALSCREEN 79
\r
1063 #ifndef SM_XVIRTUALSCREEN
\r
1064 #define SM_XVIRTUALSCREEN 76
\r
1066 #ifndef SM_YVIRTUALSCREEN
\r
1067 #define SM_YVIRTUALSCREEN 77
\r
1073 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1074 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1075 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1076 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1077 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1078 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1079 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1080 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1084 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1086 HWND hwnd; /* Main window handle. */
\r
1088 WINDOWPLACEMENT wp;
\r
1091 hInst = hInstance; /* Store instance handle in our global variable */
\r
1092 programName = szAppName;
\r
1094 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1095 *filepart = NULLCHAR;
\r
1096 SetCurrentDirectory(installDir);
\r
1098 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1100 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1102 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1103 /* xboard, and older WinBoards, controlled the move sound with the
\r
1104 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1105 always turn the option on (so that the backend will call us),
\r
1106 then let the user turn the sound off by setting it to silence if
\r
1107 desired. To accommodate old winboard.ini files saved by old
\r
1108 versions of WinBoard, we also turn off the sound if the option
\r
1109 was initially set to false. [HGM] taken out of InitAppData */
\r
1110 if (!appData.ringBellAfterMoves) {
\r
1111 sounds[(int)SoundMove].name = strdup("");
\r
1112 appData.ringBellAfterMoves = TRUE;
\r
1114 if (appData.debugMode) {
\r
1115 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1116 setbuf(debugFP, NULL);
\r
1119 LoadLanguageFile(appData.language);
\r
1123 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1124 // InitEngineUCI( installDir, &second );
\r
1126 /* Create a main window for this application instance. */
\r
1127 hwnd = CreateWindow(szAppName, szTitle,
\r
1128 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1129 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1130 NULL, NULL, hInstance, NULL);
\r
1133 /* If window could not be created, return "failure" */
\r
1138 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1139 LoadLogo(&first, 0, FALSE);
\r
1140 LoadLogo(&second, 1, appData.icsActive);
\r
1144 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1145 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1146 iconCurrent = iconWhite;
\r
1147 InitDrawingColors();
\r
1149 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1150 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1151 /* Compute window size for each board size, and use the largest
\r
1152 size that fits on this screen as the default. */
\r
1153 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1154 if (boardSize == (BoardSize)-1 &&
\r
1155 winH <= screenHeight
\r
1156 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1157 && winW <= screenWidth) {
\r
1158 boardSize = (BoardSize)ibs;
\r
1162 InitDrawingSizes(boardSize, 0);
\r
1163 RecentEngineMenu(appData.recentEngineList);
\r
1165 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1167 /* [AS] Load textures if specified */
\r
1170 mysrandom( (unsigned) time(NULL) );
\r
1172 /* [AS] Restore layout */
\r
1173 if( wpMoveHistory.visible ) {
\r
1174 MoveHistoryPopUp();
\r
1177 if( wpEvalGraph.visible ) {
\r
1181 if( wpEngineOutput.visible ) {
\r
1182 EngineOutputPopUp();
\r
1185 /* Make the window visible; update its client area; and return "success" */
\r
1186 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1187 wp.length = sizeof(WINDOWPLACEMENT);
\r
1189 wp.showCmd = nCmdShow;
\r
1190 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1191 wp.rcNormalPosition.left = wpMain.x;
\r
1192 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1193 wp.rcNormalPosition.top = wpMain.y;
\r
1194 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1195 SetWindowPlacement(hwndMain, &wp);
\r
1197 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1199 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1200 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1202 if (hwndConsole) {
\r
1204 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1205 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1207 ShowWindow(hwndConsole, nCmdShow);
\r
1208 SetActiveWindow(hwndConsole);
\r
1210 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1211 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1220 HMENU hmenu = GetMenu(hwndMain);
\r
1222 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1223 MF_BYCOMMAND|((appData.icsActive &&
\r
1224 *appData.icsCommPort != NULLCHAR) ?
\r
1225 MF_ENABLED : MF_GRAYED));
\r
1226 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1227 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1228 MF_CHECKED : MF_UNCHECKED));
\r
1231 //---------------------------------------------------------------------------------------------------------
\r
1233 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1234 #define XBOARD FALSE
\r
1236 #define OPTCHAR "/"
\r
1237 #define SEPCHAR "="
\r
1238 #define TOPLEVEL 0
\r
1242 // front-end part of option handling
\r
1245 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1247 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1248 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1251 lf->lfEscapement = 0;
\r
1252 lf->lfOrientation = 0;
\r
1253 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1254 lf->lfItalic = mfp->italic;
\r
1255 lf->lfUnderline = mfp->underline;
\r
1256 lf->lfStrikeOut = mfp->strikeout;
\r
1257 lf->lfCharSet = mfp->charset;
\r
1258 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1259 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1260 lf->lfQuality = DEFAULT_QUALITY;
\r
1261 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1262 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1266 CreateFontInMF(MyFont *mf)
\r
1268 LFfromMFP(&mf->lf, &mf->mfp);
\r
1269 if (mf->hf) DeleteObject(mf->hf);
\r
1270 mf->hf = CreateFontIndirect(&mf->lf);
\r
1273 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1275 colorVariable[] = {
\r
1276 &whitePieceColor,
\r
1277 &blackPieceColor,
\r
1278 &lightSquareColor,
\r
1279 &darkSquareColor,
\r
1280 &highlightSquareColor,
\r
1281 &premoveHighlightColor,
\r
1283 &consoleBackgroundColor,
\r
1284 &appData.fontForeColorWhite,
\r
1285 &appData.fontBackColorWhite,
\r
1286 &appData.fontForeColorBlack,
\r
1287 &appData.fontBackColorBlack,
\r
1288 &appData.evalHistColorWhite,
\r
1289 &appData.evalHistColorBlack,
\r
1290 &appData.highlightArrowColor,
\r
1293 /* Command line font name parser. NULL name means do nothing.
\r
1294 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1295 For backward compatibility, syntax without the colon is also
\r
1296 accepted, but font names with digits in them won't work in that case.
\r
1299 ParseFontName(char *name, MyFontParams *mfp)
\r
1302 if (name == NULL) return;
\r
1304 q = strchr(p, ':');
\r
1306 if (q - p >= sizeof(mfp->faceName))
\r
1307 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1308 memcpy(mfp->faceName, p, q - p);
\r
1309 mfp->faceName[q - p] = NULLCHAR;
\r
1312 q = mfp->faceName;
\r
1314 while (*p && !isdigit(*p)) {
\r
1316 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1317 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1319 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1322 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1323 mfp->pointSize = (float) atof(p);
\r
1324 mfp->bold = (strchr(p, 'b') != NULL);
\r
1325 mfp->italic = (strchr(p, 'i') != NULL);
\r
1326 mfp->underline = (strchr(p, 'u') != NULL);
\r
1327 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1328 mfp->charset = DEFAULT_CHARSET;
\r
1329 q = strchr(p, 'c');
\r
1331 mfp->charset = (BYTE) atoi(q+1);
\r
1335 ParseFont(char *name, int number)
\r
1336 { // wrapper to shield back-end from 'font'
\r
1337 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1342 { // in WB we have a 2D array of fonts; this initializes their description
\r
1344 /* Point font array elements to structures and
\r
1345 parse default font names */
\r
1346 for (i=0; i<NUM_FONTS; i++) {
\r
1347 for (j=0; j<NUM_SIZES; j++) {
\r
1348 font[j][i] = &fontRec[j][i];
\r
1349 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1356 { // here we create the actual fonts from the selected descriptions
\r
1358 for (i=0; i<NUM_FONTS; i++) {
\r
1359 for (j=0; j<NUM_SIZES; j++) {
\r
1360 CreateFontInMF(font[j][i]);
\r
1364 /* Color name parser.
\r
1365 X version accepts X color names, but this one
\r
1366 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1368 ParseColorName(char *name)
\r
1370 int red, green, blue, count;
\r
1371 char buf[MSG_SIZ];
\r
1373 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1375 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1376 &red, &green, &blue);
\r
1379 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1380 DisplayError(buf, 0);
\r
1381 return RGB(0, 0, 0);
\r
1383 return PALETTERGB(red, green, blue);
\r
1387 ParseColor(int n, char *name)
\r
1388 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1389 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1393 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1395 char *e = argValue;
\r
1399 if (*e == 'b') eff |= CFE_BOLD;
\r
1400 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1401 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1402 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1403 else if (*e == '#' || isdigit(*e)) break;
\r
1407 *color = ParseColorName(e);
\r
1411 ParseTextAttribs(ColorClass cc, char *s)
\r
1412 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1413 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1414 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1418 ParseBoardSize(void *addr, char *name)
\r
1419 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1420 BoardSize bs = SizeTiny;
\r
1421 while (sizeInfo[bs].name != NULL) {
\r
1422 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1423 *(BoardSize *)addr = bs;
\r
1428 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1433 { // [HGM] import name from appData first
\r
1436 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1437 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1438 textAttribs[cc].sound.data = NULL;
\r
1439 MyLoadSound(&textAttribs[cc].sound);
\r
1441 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1442 textAttribs[cc].sound.name = strdup("");
\r
1443 textAttribs[cc].sound.data = NULL;
\r
1445 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1446 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1447 sounds[sc].data = NULL;
\r
1448 MyLoadSound(&sounds[sc]);
\r
1453 SetCommPortDefaults()
\r
1455 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1456 dcb.DCBlength = sizeof(DCB);
\r
1457 dcb.BaudRate = 9600;
\r
1458 dcb.fBinary = TRUE;
\r
1459 dcb.fParity = FALSE;
\r
1460 dcb.fOutxCtsFlow = FALSE;
\r
1461 dcb.fOutxDsrFlow = FALSE;
\r
1462 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1463 dcb.fDsrSensitivity = FALSE;
\r
1464 dcb.fTXContinueOnXoff = TRUE;
\r
1465 dcb.fOutX = FALSE;
\r
1467 dcb.fNull = FALSE;
\r
1468 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1469 dcb.fAbortOnError = FALSE;
\r
1471 dcb.Parity = SPACEPARITY;
\r
1472 dcb.StopBits = ONESTOPBIT;
\r
1475 // [HGM] args: these three cases taken out to stay in front-end
\r
1477 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1478 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1479 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1480 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1482 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1483 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1484 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1485 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1486 ad->argName, mfp->faceName, mfp->pointSize,
\r
1487 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1488 mfp->bold ? "b" : "",
\r
1489 mfp->italic ? "i" : "",
\r
1490 mfp->underline ? "u" : "",
\r
1491 mfp->strikeout ? "s" : "",
\r
1492 (int)mfp->charset);
\r
1498 { // [HGM] copy the names from the internal WB variables to appData
\r
1501 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1502 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1503 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1504 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1508 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1509 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1510 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1511 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1512 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1513 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1514 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1515 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1516 (ta->effects) ? " " : "",
\r
1517 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1521 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1522 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1523 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1524 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1525 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1529 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1530 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1531 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1535 ParseCommPortSettings(char *s)
\r
1536 { // wrapper to keep dcb from back-end
\r
1537 ParseCommSettings(s, &dcb);
\r
1542 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1543 GetActualPlacement(hwndMain, &wpMain);
\r
1544 GetActualPlacement(hwndConsole, &wpConsole);
\r
1545 GetActualPlacement(commentDialog, &wpComment);
\r
1546 GetActualPlacement(editTagsDialog, &wpTags);
\r
1547 GetActualPlacement(gameListDialog, &wpGameList);
\r
1548 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1549 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1550 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1554 PrintCommPortSettings(FILE *f, char *name)
\r
1555 { // wrapper to shield back-end from DCB
\r
1556 PrintCommSettings(f, name, &dcb);
\r
1560 MySearchPath(char *installDir, char *name, char *fullname)
\r
1562 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1563 if(name[0]== '%') {
\r
1564 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1565 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1566 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1567 *strchr(buf, '%') = 0;
\r
1568 strcat(fullname, getenv(buf));
\r
1569 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1571 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1572 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1573 return (int) strlen(fullname);
\r
1575 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1579 MyGetFullPathName(char *name, char *fullname)
\r
1582 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1587 { // [HGM] args: allows testing if main window is realized from back-end
\r
1588 return hwndMain != NULL;
\r
1592 PopUpStartupDialog()
\r
1596 LoadLanguageFile(appData.language);
\r
1597 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1598 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1599 FreeProcInstance(lpProc);
\r
1602 /*---------------------------------------------------------------------------*\
\r
1604 * GDI board drawing routines
\r
1606 \*---------------------------------------------------------------------------*/
\r
1608 /* [AS] Draw square using background texture */
\r
1609 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1614 return; /* Should never happen! */
\r
1617 SetGraphicsMode( dst, GM_ADVANCED );
\r
1624 /* X reflection */
\r
1629 x.eDx = (FLOAT) dw + dx - 1;
\r
1632 SetWorldTransform( dst, &x );
\r
1635 /* Y reflection */
\r
1641 x.eDy = (FLOAT) dh + dy - 1;
\r
1643 SetWorldTransform( dst, &x );
\r
1651 x.eDx = (FLOAT) dx;
\r
1652 x.eDy = (FLOAT) dy;
\r
1655 SetWorldTransform( dst, &x );
\r
1659 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1667 SetWorldTransform( dst, &x );
\r
1669 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1672 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1674 PM_WP = (int) WhitePawn,
\r
1675 PM_WN = (int) WhiteKnight,
\r
1676 PM_WB = (int) WhiteBishop,
\r
1677 PM_WR = (int) WhiteRook,
\r
1678 PM_WQ = (int) WhiteQueen,
\r
1679 PM_WF = (int) WhiteFerz,
\r
1680 PM_WW = (int) WhiteWazir,
\r
1681 PM_WE = (int) WhiteAlfil,
\r
1682 PM_WM = (int) WhiteMan,
\r
1683 PM_WO = (int) WhiteCannon,
\r
1684 PM_WU = (int) WhiteUnicorn,
\r
1685 PM_WH = (int) WhiteNightrider,
\r
1686 PM_WA = (int) WhiteAngel,
\r
1687 PM_WC = (int) WhiteMarshall,
\r
1688 PM_WAB = (int) WhiteCardinal,
\r
1689 PM_WD = (int) WhiteDragon,
\r
1690 PM_WL = (int) WhiteLance,
\r
1691 PM_WS = (int) WhiteCobra,
\r
1692 PM_WV = (int) WhiteFalcon,
\r
1693 PM_WSG = (int) WhiteSilver,
\r
1694 PM_WG = (int) WhiteGrasshopper,
\r
1695 PM_WK = (int) WhiteKing,
\r
1696 PM_BP = (int) BlackPawn,
\r
1697 PM_BN = (int) BlackKnight,
\r
1698 PM_BB = (int) BlackBishop,
\r
1699 PM_BR = (int) BlackRook,
\r
1700 PM_BQ = (int) BlackQueen,
\r
1701 PM_BF = (int) BlackFerz,
\r
1702 PM_BW = (int) BlackWazir,
\r
1703 PM_BE = (int) BlackAlfil,
\r
1704 PM_BM = (int) BlackMan,
\r
1705 PM_BO = (int) BlackCannon,
\r
1706 PM_BU = (int) BlackUnicorn,
\r
1707 PM_BH = (int) BlackNightrider,
\r
1708 PM_BA = (int) BlackAngel,
\r
1709 PM_BC = (int) BlackMarshall,
\r
1710 PM_BG = (int) BlackGrasshopper,
\r
1711 PM_BAB = (int) BlackCardinal,
\r
1712 PM_BD = (int) BlackDragon,
\r
1713 PM_BL = (int) BlackLance,
\r
1714 PM_BS = (int) BlackCobra,
\r
1715 PM_BV = (int) BlackFalcon,
\r
1716 PM_BSG = (int) BlackSilver,
\r
1717 PM_BK = (int) BlackKing
\r
1720 static HFONT hPieceFont = NULL;
\r
1721 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1722 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1723 static int fontBitmapSquareSize = 0;
\r
1724 static char pieceToFontChar[(int) EmptySquare] =
\r
1725 { 'p', 'n', 'b', 'r', 'q',
\r
1726 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1727 'k', 'o', 'm', 'v', 't', 'w',
\r
1728 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1731 extern BOOL SetCharTable( char *table, const char * map );
\r
1732 /* [HGM] moved to backend.c */
\r
1734 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1737 BYTE r1 = GetRValue( color );
\r
1738 BYTE g1 = GetGValue( color );
\r
1739 BYTE b1 = GetBValue( color );
\r
1745 /* Create a uniform background first */
\r
1746 hbrush = CreateSolidBrush( color );
\r
1747 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1748 FillRect( hdc, &rc, hbrush );
\r
1749 DeleteObject( hbrush );
\r
1752 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1753 int steps = squareSize / 2;
\r
1756 for( i=0; i<steps; i++ ) {
\r
1757 BYTE r = r1 - (r1-r2) * i / steps;
\r
1758 BYTE g = g1 - (g1-g2) * i / steps;
\r
1759 BYTE b = b1 - (b1-b2) * i / steps;
\r
1761 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1762 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1763 FillRect( hdc, &rc, hbrush );
\r
1764 DeleteObject(hbrush);
\r
1767 else if( mode == 2 ) {
\r
1768 /* Diagonal gradient, good more or less for every piece */
\r
1769 POINT triangle[3];
\r
1770 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1771 HBRUSH hbrush_old;
\r
1772 int steps = squareSize;
\r
1775 triangle[0].x = squareSize - steps;
\r
1776 triangle[0].y = squareSize;
\r
1777 triangle[1].x = squareSize;
\r
1778 triangle[1].y = squareSize;
\r
1779 triangle[2].x = squareSize;
\r
1780 triangle[2].y = squareSize - steps;
\r
1782 for( i=0; i<steps; i++ ) {
\r
1783 BYTE r = r1 - (r1-r2) * i / steps;
\r
1784 BYTE g = g1 - (g1-g2) * i / steps;
\r
1785 BYTE b = b1 - (b1-b2) * i / steps;
\r
1787 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1788 hbrush_old = SelectObject( hdc, hbrush );
\r
1789 Polygon( hdc, triangle, 3 );
\r
1790 SelectObject( hdc, hbrush_old );
\r
1791 DeleteObject(hbrush);
\r
1796 SelectObject( hdc, hpen );
\r
1801 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1802 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1803 piece: follow the steps as explained below.
\r
1805 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1809 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1813 int backColor = whitePieceColor;
\r
1814 int foreColor = blackPieceColor;
\r
1816 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1817 backColor = appData.fontBackColorWhite;
\r
1818 foreColor = appData.fontForeColorWhite;
\r
1820 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1821 backColor = appData.fontBackColorBlack;
\r
1822 foreColor = appData.fontForeColorBlack;
\r
1826 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1828 hbm_old = SelectObject( hdc, hbm );
\r
1832 rc.right = squareSize;
\r
1833 rc.bottom = squareSize;
\r
1835 /* Step 1: background is now black */
\r
1836 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1838 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1840 pt.x = (squareSize - sz.cx) / 2;
\r
1841 pt.y = (squareSize - sz.cy) / 2;
\r
1843 SetBkMode( hdc, TRANSPARENT );
\r
1844 SetTextColor( hdc, chroma );
\r
1845 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1846 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1848 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1849 /* Step 3: the area outside the piece is filled with white */
\r
1850 // FloodFill( hdc, 0, 0, chroma );
\r
1851 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1852 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1853 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1854 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1855 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1857 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1858 but if the start point is not inside the piece we're lost!
\r
1859 There should be a better way to do this... if we could create a region or path
\r
1860 from the fill operation we would be fine for example.
\r
1862 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1863 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1865 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1866 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1867 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1869 SelectObject( dc2, bm2 );
\r
1870 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1871 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1872 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1873 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1874 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1877 DeleteObject( bm2 );
\r
1880 SetTextColor( hdc, 0 );
\r
1882 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1883 draw the piece again in black for safety.
\r
1885 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1887 SelectObject( hdc, hbm_old );
\r
1889 if( hPieceMask[index] != NULL ) {
\r
1890 DeleteObject( hPieceMask[index] );
\r
1893 hPieceMask[index] = hbm;
\r
1896 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1898 SelectObject( hdc, hbm );
\r
1901 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1902 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1903 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1905 SelectObject( dc1, hPieceMask[index] );
\r
1906 SelectObject( dc2, bm2 );
\r
1907 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1908 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1911 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1912 the piece background and deletes (makes transparent) the rest.
\r
1913 Thanks to that mask, we are free to paint the background with the greates
\r
1914 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1915 We use this, to make gradients and give the pieces a "roundish" look.
\r
1917 SetPieceBackground( hdc, backColor, 2 );
\r
1918 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1922 DeleteObject( bm2 );
\r
1925 SetTextColor( hdc, foreColor );
\r
1926 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1928 SelectObject( hdc, hbm_old );
\r
1930 if( hPieceFace[index] != NULL ) {
\r
1931 DeleteObject( hPieceFace[index] );
\r
1934 hPieceFace[index] = hbm;
\r
1937 static int TranslatePieceToFontPiece( int piece )
\r
1967 case BlackMarshall:
\r
1971 case BlackNightrider:
\r
1977 case BlackUnicorn:
\r
1981 case BlackGrasshopper:
\r
1993 case BlackCardinal:
\r
2000 case WhiteMarshall:
\r
2004 case WhiteNightrider:
\r
2010 case WhiteUnicorn:
\r
2014 case WhiteGrasshopper:
\r
2026 case WhiteCardinal:
\r
2035 void CreatePiecesFromFont()
\r
2038 HDC hdc_window = NULL;
\r
2044 if( fontBitmapSquareSize < 0 ) {
\r
2045 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2049 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2050 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2051 fontBitmapSquareSize = -1;
\r
2055 if( fontBitmapSquareSize != squareSize ) {
\r
2056 hdc_window = GetDC( hwndMain );
\r
2057 hdc = CreateCompatibleDC( hdc_window );
\r
2059 if( hPieceFont != NULL ) {
\r
2060 DeleteObject( hPieceFont );
\r
2063 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2064 hPieceMask[i] = NULL;
\r
2065 hPieceFace[i] = NULL;
\r
2071 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2072 fontHeight = appData.fontPieceSize;
\r
2075 fontHeight = (fontHeight * squareSize) / 100;
\r
2077 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2079 lf.lfEscapement = 0;
\r
2080 lf.lfOrientation = 0;
\r
2081 lf.lfWeight = FW_NORMAL;
\r
2083 lf.lfUnderline = 0;
\r
2084 lf.lfStrikeOut = 0;
\r
2085 lf.lfCharSet = DEFAULT_CHARSET;
\r
2086 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2087 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2088 lf.lfQuality = PROOF_QUALITY;
\r
2089 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2090 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2091 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2093 hPieceFont = CreateFontIndirect( &lf );
\r
2095 if( hPieceFont == NULL ) {
\r
2096 fontBitmapSquareSize = -2;
\r
2099 /* Setup font-to-piece character table */
\r
2100 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2101 /* No (or wrong) global settings, try to detect the font */
\r
2102 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2104 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2106 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2107 /* DiagramTT* family */
\r
2108 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2110 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2111 /* Fairy symbols */
\r
2112 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2114 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2115 /* Good Companion (Some characters get warped as literal :-( */
\r
2116 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2117 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2118 SetCharTable(pieceToFontChar, s);
\r
2121 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2122 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2126 /* Create bitmaps */
\r
2127 hfont_old = SelectObject( hdc, hPieceFont );
\r
2128 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2129 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2130 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2132 SelectObject( hdc, hfont_old );
\r
2134 fontBitmapSquareSize = squareSize;
\r
2138 if( hdc != NULL ) {
\r
2142 if( hdc_window != NULL ) {
\r
2143 ReleaseDC( hwndMain, hdc_window );
\r
2148 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2150 char name[128], buf[MSG_SIZ];
\r
2152 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2153 if(appData.pieceDirectory[0]) {
\r
2155 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2156 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2157 if(res) return res;
\r
2159 if (gameInfo.event &&
\r
2160 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2161 strcmp(name, "k80s") == 0) {
\r
2162 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2164 return LoadBitmap(hinst, name);
\r
2168 /* Insert a color into the program's logical palette
\r
2169 structure. This code assumes the given color is
\r
2170 the result of the RGB or PALETTERGB macro, and it
\r
2171 knows how those macros work (which is documented).
\r
2174 InsertInPalette(COLORREF color)
\r
2176 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2178 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2179 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2180 pLogPal->palNumEntries--;
\r
2184 pe->peFlags = (char) 0;
\r
2185 pe->peRed = (char) (0xFF & color);
\r
2186 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2187 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2193 InitDrawingColors()
\r
2195 if (pLogPal == NULL) {
\r
2196 /* Allocate enough memory for a logical palette with
\r
2197 * PALETTESIZE entries and set the size and version fields
\r
2198 * of the logical palette structure.
\r
2200 pLogPal = (NPLOGPALETTE)
\r
2201 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2202 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2203 pLogPal->palVersion = 0x300;
\r
2205 pLogPal->palNumEntries = 0;
\r
2207 InsertInPalette(lightSquareColor);
\r
2208 InsertInPalette(darkSquareColor);
\r
2209 InsertInPalette(whitePieceColor);
\r
2210 InsertInPalette(blackPieceColor);
\r
2211 InsertInPalette(highlightSquareColor);
\r
2212 InsertInPalette(premoveHighlightColor);
\r
2214 /* create a logical color palette according the information
\r
2215 * in the LOGPALETTE structure.
\r
2217 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2219 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2220 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2221 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2222 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2223 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2224 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2225 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2226 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2227 /* [AS] Force rendering of the font-based pieces */
\r
2228 if( fontBitmapSquareSize > 0 ) {
\r
2229 fontBitmapSquareSize = 0;
\r
2235 BoardWidth(int boardSize, int n)
\r
2236 { /* [HGM] argument n added to allow different width and height */
\r
2237 int lineGap = sizeInfo[boardSize].lineGap;
\r
2239 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2240 lineGap = appData.overrideLineGap;
\r
2243 return (n + 1) * lineGap +
\r
2244 n * sizeInfo[boardSize].squareSize;
\r
2247 /* Respond to board resize by dragging edge */
\r
2249 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2251 BoardSize newSize = NUM_SIZES - 1;
\r
2252 static int recurse = 0;
\r
2253 if (IsIconic(hwndMain)) return;
\r
2254 if (recurse > 0) return;
\r
2256 while (newSize > 0) {
\r
2257 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2258 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2259 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2262 boardSize = newSize;
\r
2263 InitDrawingSizes(boardSize, flags);
\r
2268 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2271 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2273 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2274 ChessSquare piece;
\r
2275 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2277 SIZE clockSize, messageSize;
\r
2279 char buf[MSG_SIZ];
\r
2281 HMENU hmenu = GetMenu(hwndMain);
\r
2282 RECT crect, wrect, oldRect;
\r
2284 LOGBRUSH logbrush;
\r
2285 VariantClass v = gameInfo.variant;
\r
2287 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2288 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2290 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2291 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2292 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2293 oldBoardSize = boardSize;
\r
2295 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2296 { // correct board size to one where built-in pieces exist
\r
2297 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2298 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2299 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2300 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2301 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2302 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2303 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2304 boardSize = SizeMiddling;
\r
2307 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2309 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2310 oldRect.top = wpMain.y;
\r
2311 oldRect.right = wpMain.x + wpMain.width;
\r
2312 oldRect.bottom = wpMain.y + wpMain.height;
\r
2314 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2315 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2316 squareSize = sizeInfo[boardSize].squareSize;
\r
2317 lineGap = sizeInfo[boardSize].lineGap;
\r
2318 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2319 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2321 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2322 lineGap = appData.overrideLineGap;
\r
2325 if (tinyLayout != oldTinyLayout) {
\r
2326 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2328 style &= ~WS_SYSMENU;
\r
2329 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2330 "&Minimize\tCtrl+F4");
\r
2332 style |= WS_SYSMENU;
\r
2333 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2335 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2337 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2338 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2339 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2341 DrawMenuBar(hwndMain);
\r
2344 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2345 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2347 /* Get text area sizes */
\r
2348 hdc = GetDC(hwndMain);
\r
2349 if (appData.clockMode) {
\r
2350 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2352 snprintf(buf, MSG_SIZ, _("White"));
\r
2354 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2355 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2356 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2357 str = _("We only care about the height here");
\r
2358 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2359 SelectObject(hdc, oldFont);
\r
2360 ReleaseDC(hwndMain, hdc);
\r
2362 /* Compute where everything goes */
\r
2363 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2364 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2365 logoHeight = 2*clockSize.cy;
\r
2366 leftLogoRect.left = OUTER_MARGIN;
\r
2367 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2368 leftLogoRect.top = OUTER_MARGIN;
\r
2369 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2371 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2372 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2373 rightLogoRect.top = OUTER_MARGIN;
\r
2374 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2377 whiteRect.left = leftLogoRect.right;
\r
2378 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2379 whiteRect.top = OUTER_MARGIN;
\r
2380 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2382 blackRect.right = rightLogoRect.left;
\r
2383 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2384 blackRect.top = whiteRect.top;
\r
2385 blackRect.bottom = whiteRect.bottom;
\r
2387 whiteRect.left = OUTER_MARGIN;
\r
2388 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2389 whiteRect.top = OUTER_MARGIN;
\r
2390 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2392 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2393 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2394 blackRect.top = whiteRect.top;
\r
2395 blackRect.bottom = whiteRect.bottom;
\r
2397 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2400 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2401 if (appData.showButtonBar) {
\r
2402 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2403 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2405 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2407 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2408 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2410 boardRect.left = OUTER_MARGIN;
\r
2411 boardRect.right = boardRect.left + boardWidth;
\r
2412 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2413 boardRect.bottom = boardRect.top + boardHeight;
\r
2415 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2416 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2417 oldTinyLayout = tinyLayout;
\r
2418 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2419 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2420 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2421 winW *= 1 + twoBoards;
\r
2422 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2423 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2424 wpMain.height = winH; // without disturbing window attachments
\r
2425 GetWindowRect(hwndMain, &wrect);
\r
2426 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2427 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2429 // [HGM] placement: let attached windows follow size change.
\r
2430 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2431 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2432 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2433 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2434 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2436 /* compensate if menu bar wrapped */
\r
2437 GetClientRect(hwndMain, &crect);
\r
2438 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2439 wpMain.height += offby;
\r
2441 case WMSZ_TOPLEFT:
\r
2442 SetWindowPos(hwndMain, NULL,
\r
2443 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2444 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2447 case WMSZ_TOPRIGHT:
\r
2449 SetWindowPos(hwndMain, NULL,
\r
2450 wrect.left, wrect.bottom - wpMain.height,
\r
2451 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2454 case WMSZ_BOTTOMLEFT:
\r
2456 SetWindowPos(hwndMain, NULL,
\r
2457 wrect.right - wpMain.width, wrect.top,
\r
2458 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2461 case WMSZ_BOTTOMRIGHT:
\r
2465 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2466 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2471 for (i = 0; i < N_BUTTONS; i++) {
\r
2472 if (buttonDesc[i].hwnd != NULL) {
\r
2473 DestroyWindow(buttonDesc[i].hwnd);
\r
2474 buttonDesc[i].hwnd = NULL;
\r
2476 if (appData.showButtonBar) {
\r
2477 buttonDesc[i].hwnd =
\r
2478 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2479 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2480 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2481 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2482 (HMENU) buttonDesc[i].id,
\r
2483 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2485 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2486 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2487 MAKELPARAM(FALSE, 0));
\r
2489 if (buttonDesc[i].id == IDM_Pause)
\r
2490 hwndPause = buttonDesc[i].hwnd;
\r
2491 buttonDesc[i].wndproc = (WNDPROC)
\r
2492 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2495 if (gridPen != NULL) DeleteObject(gridPen);
\r
2496 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2497 if (premovePen != NULL) DeleteObject(premovePen);
\r
2498 if (lineGap != 0) {
\r
2499 logbrush.lbStyle = BS_SOLID;
\r
2500 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2502 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2503 lineGap, &logbrush, 0, NULL);
\r
2504 logbrush.lbColor = highlightSquareColor;
\r
2506 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2507 lineGap, &logbrush, 0, NULL);
\r
2509 logbrush.lbColor = premoveHighlightColor;
\r
2511 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2512 lineGap, &logbrush, 0, NULL);
\r
2514 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2515 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2516 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2517 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2518 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2519 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2520 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2521 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2523 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2524 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2525 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2526 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2527 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2528 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2529 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2530 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2534 /* [HGM] Licensing requirement */
\r
2536 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2539 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2541 GothicPopUp( "", VariantNormal);
\r
2544 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2546 /* Load piece bitmaps for this board size */
\r
2547 for (i=0; i<=2; i++) {
\r
2548 for (piece = WhitePawn;
\r
2549 (int) piece < (int) BlackPawn;
\r
2550 piece = (ChessSquare) ((int) piece + 1)) {
\r
2551 if (pieceBitmap[i][piece] != NULL)
\r
2552 DeleteObject(pieceBitmap[i][piece]);
\r
2556 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2557 // Orthodox Chess pieces
\r
2558 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2559 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2560 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2561 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2562 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2563 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2564 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2565 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2566 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2567 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2568 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2569 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2570 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2571 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2572 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2573 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2574 // in Shogi, Hijack the unused Queen for Lance
\r
2575 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2576 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2577 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2584 if(squareSize <= 72 && squareSize >= 33) {
\r
2585 /* A & C are available in most sizes now */
\r
2586 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2587 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2593 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2596 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2599 } else { // Smirf-like
\r
2600 if(gameInfo.variant == VariantSChess) {
\r
2601 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2602 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2603 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2605 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2606 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2607 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2610 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2611 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2614 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2615 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2616 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2617 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2618 } else { // WinBoard standard
\r
2619 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2620 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2621 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2626 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2627 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2630 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2636 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2637 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2638 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2639 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2640 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2641 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2642 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2643 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2644 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2645 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2646 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2647 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2648 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2649 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2650 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2651 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2652 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2653 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2654 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2655 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2656 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2658 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2659 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2660 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2661 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2662 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2663 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2664 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2665 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2666 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2667 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2668 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2669 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2670 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2672 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2673 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2674 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2675 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2676 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2677 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2678 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2679 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2680 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2681 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2682 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2683 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2686 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2687 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2688 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2689 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2690 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2691 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2692 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2693 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2694 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2695 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2696 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2697 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2698 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2699 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2700 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2704 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2705 /* special Shogi support in this size */
\r
2706 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2707 for (piece = WhitePawn;
\r
2708 (int) piece < (int) BlackPawn;
\r
2709 piece = (ChessSquare) ((int) piece + 1)) {
\r
2710 if (pieceBitmap[i][piece] != NULL)
\r
2711 DeleteObject(pieceBitmap[i][piece]);
\r
2714 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2715 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2716 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2717 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2718 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2719 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2720 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2721 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2722 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2723 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2724 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2725 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2726 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2727 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2728 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2729 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2730 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2731 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2732 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2733 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2734 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2735 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2736 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2737 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2738 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2739 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2740 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2741 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2742 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2743 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2744 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2745 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2746 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2747 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2748 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2749 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2750 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2751 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2752 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2753 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2754 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2755 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2761 PieceBitmap(ChessSquare p, int kind)
\r
2763 if ((int) p >= (int) BlackPawn)
\r
2764 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2766 return pieceBitmap[kind][(int) p];
\r
2769 /***************************************************************/
\r
2771 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2772 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2774 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2775 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2779 SquareToPos(int row, int column, int * x, int * y)
\r
2782 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2783 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2785 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2786 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2791 DrawCoordsOnDC(HDC hdc)
\r
2793 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2794 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2795 char str[2] = { NULLCHAR, NULLCHAR };
\r
2796 int oldMode, oldAlign, x, y, start, i;
\r
2800 if (!appData.showCoords)
\r
2803 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2805 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2806 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2807 oldAlign = GetTextAlign(hdc);
\r
2808 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2810 y = boardRect.top + lineGap;
\r
2811 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2814 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2815 x += border - lineGap - 4; y += squareSize - 6;
\r
2817 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2818 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2819 str[0] = files[start + i];
\r
2820 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2821 y += squareSize + lineGap;
\r
2824 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2827 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2828 x += -border + 4; y += border - squareSize + 6;
\r
2830 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2831 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2832 str[0] = ranks[start + i];
\r
2833 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2834 x += squareSize + lineGap;
\r
2837 SelectObject(hdc, oldBrush);
\r
2838 SetBkMode(hdc, oldMode);
\r
2839 SetTextAlign(hdc, oldAlign);
\r
2840 SelectObject(hdc, oldFont);
\r
2844 DrawGridOnDC(HDC hdc)
\r
2848 if (lineGap != 0) {
\r
2849 oldPen = SelectObject(hdc, gridPen);
\r
2850 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2851 SelectObject(hdc, oldPen);
\r
2855 #define HIGHLIGHT_PEN 0
\r
2856 #define PREMOVE_PEN 1
\r
2859 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2862 HPEN oldPen, hPen;
\r
2863 if (lineGap == 0) return;
\r
2865 x1 = boardRect.left +
\r
2866 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2867 y1 = boardRect.top +
\r
2868 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2870 x1 = boardRect.left +
\r
2871 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2872 y1 = boardRect.top +
\r
2873 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2875 hPen = pen ? premovePen : highlightPen;
\r
2876 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2877 MoveToEx(hdc, x1, y1, NULL);
\r
2878 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2879 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2880 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2881 LineTo(hdc, x1, y1);
\r
2882 SelectObject(hdc, oldPen);
\r
2886 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2889 for (i=0; i<2; i++) {
\r
2890 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2891 DrawHighlightOnDC(hdc, TRUE,
\r
2892 h->sq[i].x, h->sq[i].y,
\r
2897 /* Note: sqcolor is used only in monoMode */
\r
2898 /* Note that this code is largely duplicated in woptions.c,
\r
2899 function DrawSampleSquare, so that needs to be updated too */
\r
2901 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2903 HBITMAP oldBitmap;
\r
2907 if (appData.blindfold) return;
\r
2909 /* [AS] Use font-based pieces if needed */
\r
2910 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2911 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2912 CreatePiecesFromFont();
\r
2914 if( fontBitmapSquareSize == squareSize ) {
\r
2915 int index = TranslatePieceToFontPiece(piece);
\r
2917 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2919 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2920 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2924 squareSize, squareSize,
\r
2929 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2931 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2932 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2936 squareSize, squareSize,
\r
2945 if (appData.monoMode) {
\r
2946 SelectObject(tmphdc, PieceBitmap(piece,
\r
2947 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2948 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2949 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2951 HBRUSH xBrush = whitePieceBrush;
\r
2952 tmpSize = squareSize;
\r
2953 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2955 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2956 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2957 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2958 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2959 x += (squareSize - minorSize)>>1;
\r
2960 y += squareSize - minorSize - 2;
\r
2961 tmpSize = minorSize;
\r
2963 if (color || appData.allWhite ) {
\r
2964 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2966 oldBrush = SelectObject(hdc, xBrush);
\r
2967 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2968 if(appData.upsideDown && color==flipView)
\r
2969 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2971 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2972 /* Use black for outline of white pieces */
\r
2973 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2974 if(appData.upsideDown && color==flipView)
\r
2975 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2977 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2978 } else if(appData.pieceDirectory[0]) {
\r
2979 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2980 oldBrush = SelectObject(hdc, xBrush);
\r
2981 if(appData.upsideDown && color==flipView)
\r
2982 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2984 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2985 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2986 if(appData.upsideDown && color==flipView)
\r
2987 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2989 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2991 /* Use square color for details of black pieces */
\r
2992 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2993 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2994 if(appData.upsideDown && !flipView)
\r
2995 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2997 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2999 SelectObject(hdc, oldBrush);
\r
3000 SelectObject(tmphdc, oldBitmap);
\r
3004 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3005 int GetBackTextureMode( int algo )
\r
3007 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3011 case BACK_TEXTURE_MODE_PLAIN:
\r
3012 result = 1; /* Always use identity map */
\r
3014 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3015 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3023 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3024 to handle redraws cleanly (as random numbers would always be different).
\r
3026 VOID RebuildTextureSquareInfo()
\r
3036 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3038 if( liteBackTexture != NULL ) {
\r
3039 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3040 lite_w = bi.bmWidth;
\r
3041 lite_h = bi.bmHeight;
\r
3045 if( darkBackTexture != NULL ) {
\r
3046 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3047 dark_w = bi.bmWidth;
\r
3048 dark_h = bi.bmHeight;
\r
3052 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3053 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3054 if( (col + row) & 1 ) {
\r
3056 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3057 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3058 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3060 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3061 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3062 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3064 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3065 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3070 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3071 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3072 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3074 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3075 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3076 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3078 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3079 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3086 /* [AS] Arrow highlighting support */
\r
3088 static double A_WIDTH = 5; /* Width of arrow body */
\r
3090 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3091 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3093 static double Sqr( double x )
\r
3098 static int Round( double x )
\r
3100 return (int) (x + 0.5);
\r
3103 /* Draw an arrow between two points using current settings */
\r
3104 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3107 double dx, dy, j, k, x, y;
\r
3109 if( d_x == s_x ) {
\r
3110 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3112 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3115 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3116 arrow[1].y = d_y - h;
\r
3118 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3119 arrow[2].y = d_y - h;
\r
3124 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3125 arrow[5].y = d_y - h;
\r
3127 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3128 arrow[4].y = d_y - h;
\r
3130 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3133 else if( d_y == s_y ) {
\r
3134 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3137 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3139 arrow[1].x = d_x - w;
\r
3140 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3142 arrow[2].x = d_x - w;
\r
3143 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3148 arrow[5].x = d_x - w;
\r
3149 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3151 arrow[4].x = d_x - w;
\r
3152 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3155 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3158 /* [AS] Needed a lot of paper for this! :-) */
\r
3159 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3160 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3162 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3164 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3169 arrow[0].x = Round(x - j);
\r
3170 arrow[0].y = Round(y + j*dx);
\r
3172 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3173 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3176 x = (double) d_x - k;
\r
3177 y = (double) d_y - k*dy;
\r
3180 x = (double) d_x + k;
\r
3181 y = (double) d_y + k*dy;
\r
3184 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3186 arrow[6].x = Round(x - j);
\r
3187 arrow[6].y = Round(y + j*dx);
\r
3189 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3190 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3192 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3193 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3198 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3199 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3202 Polygon( hdc, arrow, 7 );
\r
3205 /* [AS] Draw an arrow between two squares */
\r
3206 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3208 int s_x, s_y, d_x, d_y;
\r
3215 if( s_col == d_col && s_row == d_row ) {
\r
3219 /* Get source and destination points */
\r
3220 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3221 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3224 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3226 else if( d_y < s_y ) {
\r
3227 d_y += squareSize / 2 + squareSize / 4;
\r
3230 d_y += squareSize / 2;
\r
3234 d_x += squareSize / 2 - squareSize / 4;
\r
3236 else if( d_x < s_x ) {
\r
3237 d_x += squareSize / 2 + squareSize / 4;
\r
3240 d_x += squareSize / 2;
\r
3243 s_x += squareSize / 2;
\r
3244 s_y += squareSize / 2;
\r
3246 /* Adjust width */
\r
3247 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3250 stLB.lbStyle = BS_SOLID;
\r
3251 stLB.lbColor = appData.highlightArrowColor;
\r
3254 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3255 holdpen = SelectObject( hdc, hpen );
\r
3256 hbrush = CreateBrushIndirect( &stLB );
\r
3257 holdbrush = SelectObject( hdc, hbrush );
\r
3259 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3261 SelectObject( hdc, holdpen );
\r
3262 SelectObject( hdc, holdbrush );
\r
3263 DeleteObject( hpen );
\r
3264 DeleteObject( hbrush );
\r
3267 BOOL HasHighlightInfo()
\r
3269 BOOL result = FALSE;
\r
3271 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3272 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3280 BOOL IsDrawArrowEnabled()
\r
3282 BOOL result = FALSE;
\r
3284 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3291 VOID DrawArrowHighlight( HDC hdc )
\r
3293 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3294 DrawArrowBetweenSquares( hdc,
\r
3295 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3296 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3300 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3302 HRGN result = NULL;
\r
3304 if( HasHighlightInfo() ) {
\r
3305 int x1, y1, x2, y2;
\r
3306 int sx, sy, dx, dy;
\r
3308 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3309 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3311 sx = MIN( x1, x2 );
\r
3312 sy = MIN( y1, y2 );
\r
3313 dx = MAX( x1, x2 ) + squareSize;
\r
3314 dy = MAX( y1, y2 ) + squareSize;
\r
3316 result = CreateRectRgn( sx, sy, dx, dy );
\r
3323 Warning: this function modifies the behavior of several other functions.
\r
3325 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3326 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3327 repaint is scattered all over the place, which is not good for features such as
\r
3328 "arrow highlighting" that require a full repaint of the board.
\r
3330 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3331 user interaction, when speed is not so important) but especially to avoid errors
\r
3332 in the displayed graphics.
\r
3334 In such patched places, I always try refer to this function so there is a single
\r
3335 place to maintain knowledge.
\r
3337 To restore the original behavior, just return FALSE unconditionally.
\r
3339 BOOL IsFullRepaintPreferrable()
\r
3341 BOOL result = FALSE;
\r
3343 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3344 /* Arrow may appear on the board */
\r
3352 This function is called by DrawPosition to know whether a full repaint must
\r
3355 Only DrawPosition may directly call this function, which makes use of
\r
3356 some state information. Other function should call DrawPosition specifying
\r
3357 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3359 BOOL DrawPositionNeedsFullRepaint()
\r
3361 BOOL result = FALSE;
\r
3364 Probably a slightly better policy would be to trigger a full repaint
\r
3365 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3366 but animation is fast enough that it's difficult to notice.
\r
3368 if( animInfo.piece == EmptySquare ) {
\r
3369 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3377 static HBITMAP borderBitmap;
\r
3380 DrawBackgroundOnDC(HDC hdc)
\r
3386 static char oldBorder[MSG_SIZ];
\r
3387 int w = 600, h = 600, mode;
\r
3389 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3390 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3391 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3393 if(borderBitmap == NULL) { // loading failed, use white
\r
3394 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3397 tmphdc = CreateCompatibleDC(hdc);
\r
3398 hbm = SelectObject(tmphdc, borderBitmap);
\r
3399 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3403 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3404 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3405 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3406 SetStretchBltMode(hdc, mode);
\r
3407 SelectObject(tmphdc, hbm);
\r
3412 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3414 int row, column, x, y, square_color, piece_color;
\r
3415 ChessSquare piece;
\r
3417 HDC texture_hdc = NULL;
\r
3419 /* [AS] Initialize background textures if needed */
\r
3420 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3421 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3422 if( backTextureSquareSize != squareSize
\r
3423 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3424 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3425 backTextureSquareSize = squareSize;
\r
3426 RebuildTextureSquareInfo();
\r
3429 texture_hdc = CreateCompatibleDC( hdc );
\r
3432 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3433 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3435 SquareToPos(row, column, &x, &y);
\r
3437 piece = board[row][column];
\r
3439 square_color = ((column + row) % 2) == 1;
\r
3440 if( gameInfo.variant == VariantXiangqi ) {
\r
3441 square_color = !InPalace(row, column);
\r
3442 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3443 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3445 piece_color = (int) piece < (int) BlackPawn;
\r
3448 /* [HGM] holdings file: light square or black */
\r
3449 if(column == BOARD_LEFT-2) {
\r
3450 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3453 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3457 if(column == BOARD_RGHT + 1 ) {
\r
3458 if( row < gameInfo.holdingsSize )
\r
3461 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3465 if(column == BOARD_LEFT-1 ) /* left align */
\r
3466 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3467 else if( column == BOARD_RGHT) /* right align */
\r
3468 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3470 if (appData.monoMode) {
\r
3471 if (piece == EmptySquare) {
\r
3472 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3473 square_color ? WHITENESS : BLACKNESS);
\r
3475 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3478 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3479 /* [AS] Draw the square using a texture bitmap */
\r
3480 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3481 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3482 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3485 squareSize, squareSize,
\r
3488 backTextureSquareInfo[r][c].mode,
\r
3489 backTextureSquareInfo[r][c].x,
\r
3490 backTextureSquareInfo[r][c].y );
\r
3492 SelectObject( texture_hdc, hbm );
\r
3494 if (piece != EmptySquare) {
\r
3495 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3499 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3501 oldBrush = SelectObject(hdc, brush );
\r
3502 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3503 SelectObject(hdc, oldBrush);
\r
3504 if (piece != EmptySquare)
\r
3505 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3510 if( texture_hdc != NULL ) {
\r
3511 DeleteDC( texture_hdc );
\r
3515 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3516 void fputDW(FILE *f, int x)
\r
3518 fputc(x & 255, f);
\r
3519 fputc(x>>8 & 255, f);
\r
3520 fputc(x>>16 & 255, f);
\r
3521 fputc(x>>24 & 255, f);
\r
3524 #define MAX_CLIPS 200 /* more than enough */
\r
3527 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3529 // HBITMAP bufferBitmap;
\r
3534 int w = 100, h = 50;
\r
3536 if(logo == NULL) {
\r
3537 if(!logoHeight) return;
\r
3538 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3540 // GetClientRect(hwndMain, &Rect);
\r
3541 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3542 // Rect.bottom-Rect.top+1);
\r
3543 tmphdc = CreateCompatibleDC(hdc);
\r
3544 hbm = SelectObject(tmphdc, logo);
\r
3545 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3549 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3550 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3551 SelectObject(tmphdc, hbm);
\r
3559 HDC hdc = GetDC(hwndMain);
\r
3560 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3561 if(appData.autoLogo) {
\r
3563 switch(gameMode) { // pick logos based on game mode
\r
3564 case IcsObserving:
\r
3565 whiteLogo = second.programLogo; // ICS logo
\r
3566 blackLogo = second.programLogo;
\r
3569 case IcsPlayingWhite:
\r
3570 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3571 blackLogo = second.programLogo; // ICS logo
\r
3573 case IcsPlayingBlack:
\r
3574 whiteLogo = second.programLogo; // ICS logo
\r
3575 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3577 case TwoMachinesPlay:
\r
3578 if(first.twoMachinesColor[0] == 'b') {
\r
3579 whiteLogo = second.programLogo;
\r
3580 blackLogo = first.programLogo;
\r
3583 case MachinePlaysWhite:
\r
3584 blackLogo = userLogo;
\r
3586 case MachinePlaysBlack:
\r
3587 whiteLogo = userLogo;
\r
3588 blackLogo = first.programLogo;
\r
3591 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3592 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3593 ReleaseDC(hwndMain, hdc);
\r
3598 UpdateLogos(int display)
\r
3599 { // called after loading new engine(s), in tourney or from menu
\r
3600 LoadLogo(&first, 0, FALSE);
\r
3601 LoadLogo(&second, 1, appData.icsActive);
\r
3602 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3603 if(display) DisplayLogos();
\r
3606 static HDC hdcSeek;
\r
3608 // [HGM] seekgraph
\r
3609 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3612 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3613 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3614 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3615 SelectObject( hdcSeek, hp );
\r
3618 // front-end wrapper for drawing functions to do rectangles
\r
3619 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3624 if (hdcSeek == NULL) {
\r
3625 hdcSeek = GetDC(hwndMain);
\r
3626 if (!appData.monoMode) {
\r
3627 SelectPalette(hdcSeek, hPal, FALSE);
\r
3628 RealizePalette(hdcSeek);
\r
3631 hp = SelectObject( hdcSeek, gridPen );
\r
3632 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3633 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3634 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3635 SelectObject( hdcSeek, hp );
\r
3638 // front-end wrapper for putting text in graph
\r
3639 void DrawSeekText(char *buf, int x, int y)
\r
3642 SetBkMode( hdcSeek, TRANSPARENT );
\r
3643 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3644 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3647 void DrawSeekDot(int x, int y, int color)
\r
3649 int square = color & 0x80;
\r
3650 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3651 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3654 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3655 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3657 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3658 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3659 SelectObject(hdcSeek, oldBrush);
\r
3662 void DrawSeekOpen()
\r
3666 void DrawSeekClose()
\r
3671 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3673 static Board lastReq[2], lastDrawn[2];
\r
3674 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3675 static int lastDrawnFlipView = 0;
\r
3676 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3677 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3680 HBITMAP bufferBitmap;
\r
3681 HBITMAP oldBitmap;
\r
3683 HRGN clips[MAX_CLIPS];
\r
3684 ChessSquare dragged_piece = EmptySquare;
\r
3685 int nr = twoBoards*partnerUp;
\r
3687 /* I'm undecided on this - this function figures out whether a full
\r
3688 * repaint is necessary on its own, so there's no real reason to have the
\r
3689 * caller tell it that. I think this can safely be set to FALSE - but
\r
3690 * if we trust the callers not to request full repaints unnessesarily, then
\r
3691 * we could skip some clipping work. In other words, only request a full
\r
3692 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3693 * gamestart and similar) --Hawk
\r
3695 Boolean fullrepaint = repaint;
\r
3697 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3699 if( DrawPositionNeedsFullRepaint() ) {
\r
3700 fullrepaint = TRUE;
\r
3703 if (board == NULL) {
\r
3704 if (!lastReqValid[nr]) {
\r
3707 board = lastReq[nr];
\r
3709 CopyBoard(lastReq[nr], board);
\r
3710 lastReqValid[nr] = 1;
\r
3713 if (doingSizing) {
\r
3717 if (IsIconic(hwndMain)) {
\r
3721 if (hdc == NULL) {
\r
3722 hdc = GetDC(hwndMain);
\r
3723 if (!appData.monoMode) {
\r
3724 SelectPalette(hdc, hPal, FALSE);
\r
3725 RealizePalette(hdc);
\r
3729 releaseDC = FALSE;
\r
3732 /* Create some work-DCs */
\r
3733 hdcmem = CreateCompatibleDC(hdc);
\r
3734 tmphdc = CreateCompatibleDC(hdc);
\r
3736 /* If dragging is in progress, we temporarely remove the piece */
\r
3737 /* [HGM] or temporarily decrease count if stacked */
\r
3738 /* !! Moved to before board compare !! */
\r
3739 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3740 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3741 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3742 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3743 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3745 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3746 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3747 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3749 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3752 /* Figure out which squares need updating by comparing the
\r
3753 * newest board with the last drawn board and checking if
\r
3754 * flipping has changed.
\r
3756 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3757 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3758 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3759 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3760 SquareToPos(row, column, &x, &y);
\r
3761 clips[num_clips++] =
\r
3762 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3766 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3767 for (i=0; i<2; i++) {
\r
3768 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3769 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3770 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3771 lastDrawnHighlight.sq[i].y >= 0) {
\r
3772 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3773 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3774 clips[num_clips++] =
\r
3775 CreateRectRgn(x - lineGap, y - lineGap,
\r
3776 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3778 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3779 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3780 clips[num_clips++] =
\r
3781 CreateRectRgn(x - lineGap, y - lineGap,
\r
3782 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3786 for (i=0; i<2; i++) {
\r
3787 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3788 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3789 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3790 lastDrawnPremove.sq[i].y >= 0) {
\r
3791 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3792 lastDrawnPremove.sq[i].x, &x, &y);
\r
3793 clips[num_clips++] =
\r
3794 CreateRectRgn(x - lineGap, y - lineGap,
\r
3795 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3797 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3798 premoveHighlightInfo.sq[i].y >= 0) {
\r
3799 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3800 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3801 clips[num_clips++] =
\r
3802 CreateRectRgn(x - lineGap, y - lineGap,
\r
3803 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3807 } else { // nr == 1
\r
3808 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3809 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3810 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3811 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3812 for (i=0; i<2; i++) {
\r
3813 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3814 partnerHighlightInfo.sq[i].y >= 0) {
\r
3815 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3816 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3817 clips[num_clips++] =
\r
3818 CreateRectRgn(x - lineGap, y - lineGap,
\r
3819 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3821 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3822 oldPartnerHighlight.sq[i].y >= 0) {
\r
3823 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3824 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3825 clips[num_clips++] =
\r
3826 CreateRectRgn(x - lineGap, y - lineGap,
\r
3827 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3832 fullrepaint = TRUE;
\r
3835 /* Create a buffer bitmap - this is the actual bitmap
\r
3836 * being written to. When all the work is done, we can
\r
3837 * copy it to the real DC (the screen). This avoids
\r
3838 * the problems with flickering.
\r
3840 GetClientRect(hwndMain, &Rect);
\r
3841 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3842 Rect.bottom-Rect.top+1);
\r
3843 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3844 if (!appData.monoMode) {
\r
3845 SelectPalette(hdcmem, hPal, FALSE);
\r
3848 /* Create clips for dragging */
\r
3849 if (!fullrepaint) {
\r
3850 if (dragInfo.from.x >= 0) {
\r
3851 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3852 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3854 if (dragInfo.start.x >= 0) {
\r
3855 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3856 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3858 if (dragInfo.pos.x >= 0) {
\r
3859 x = dragInfo.pos.x - squareSize / 2;
\r
3860 y = dragInfo.pos.y - squareSize / 2;
\r
3861 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3863 if (dragInfo.lastpos.x >= 0) {
\r
3864 x = dragInfo.lastpos.x - squareSize / 2;
\r
3865 y = dragInfo.lastpos.y - squareSize / 2;
\r
3866 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3870 /* Are we animating a move?
\r
3872 * - remove the piece from the board (temporarely)
\r
3873 * - calculate the clipping region
\r
3875 if (!fullrepaint) {
\r
3876 if (animInfo.piece != EmptySquare) {
\r
3877 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3878 x = boardRect.left + animInfo.lastpos.x;
\r
3879 y = boardRect.top + animInfo.lastpos.y;
\r
3880 x2 = boardRect.left + animInfo.pos.x;
\r
3881 y2 = boardRect.top + animInfo.pos.y;
\r
3882 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3883 /* Slight kludge. The real problem is that after AnimateMove is
\r
3884 done, the position on the screen does not match lastDrawn.
\r
3885 This currently causes trouble only on e.p. captures in
\r
3886 atomic, where the piece moves to an empty square and then
\r
3887 explodes. The old and new positions both had an empty square
\r
3888 at the destination, but animation has drawn a piece there and
\r
3889 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3890 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3894 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3895 if (num_clips == 0)
\r
3896 fullrepaint = TRUE;
\r
3898 /* Set clipping on the memory DC */
\r
3899 if (!fullrepaint) {
\r
3900 SelectClipRgn(hdcmem, clips[0]);
\r
3901 for (x = 1; x < num_clips; x++) {
\r
3902 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3903 abort(); // this should never ever happen!
\r
3907 /* Do all the drawing to the memory DC */
\r
3908 if(explodeInfo.radius) { // [HGM] atomic
\r
3910 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3911 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3912 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3913 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3914 x += squareSize/2;
\r
3915 y += squareSize/2;
\r
3916 if(!fullrepaint) {
\r
3917 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3918 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3920 DrawGridOnDC(hdcmem);
\r
3921 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3922 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3923 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3924 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3925 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3926 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3927 SelectObject(hdcmem, oldBrush);
\r
3929 if(border) DrawBackgroundOnDC(hdcmem);
\r
3930 DrawGridOnDC(hdcmem);
\r
3931 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3932 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3933 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3935 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3936 oldPartnerHighlight = partnerHighlightInfo;
\r
3938 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3940 if(nr == 0) // [HGM] dual: markers only on left board
\r
3941 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3942 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3943 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3944 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3945 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3946 SquareToPos(row, column, &x, &y);
\r
3947 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3948 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3949 SelectObject(hdcmem, oldBrush);
\r
3954 if( appData.highlightMoveWithArrow ) {
\r
3955 DrawArrowHighlight(hdcmem);
\r
3958 DrawCoordsOnDC(hdcmem);
\r
3960 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3961 /* to make sure lastDrawn contains what is actually drawn */
\r
3963 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3964 if (dragged_piece != EmptySquare) {
\r
3965 /* [HGM] or restack */
\r
3966 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3967 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3969 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3970 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3971 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3972 x = dragInfo.pos.x - squareSize / 2;
\r
3973 y = dragInfo.pos.y - squareSize / 2;
\r
3974 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3975 ((int) dragInfo.piece < (int) BlackPawn),
\r
3976 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3979 /* Put the animated piece back into place and draw it */
\r
3980 if (animInfo.piece != EmptySquare) {
\r
3981 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3982 x = boardRect.left + animInfo.pos.x;
\r
3983 y = boardRect.top + animInfo.pos.y;
\r
3984 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3985 ((int) animInfo.piece < (int) BlackPawn),
\r
3986 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3989 /* Release the bufferBitmap by selecting in the old bitmap
\r
3990 * and delete the memory DC
\r
3992 SelectObject(hdcmem, oldBitmap);
\r
3995 /* Set clipping on the target DC */
\r
3996 if (!fullrepaint) {
\r
3997 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3999 GetRgnBox(clips[x], &rect);
\r
4000 DeleteObject(clips[x]);
\r
4001 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4002 rect.right + wpMain.width/2, rect.bottom);
\r
4004 SelectClipRgn(hdc, clips[0]);
\r
4005 for (x = 1; x < num_clips; x++) {
\r
4006 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4007 abort(); // this should never ever happen!
\r
4011 /* Copy the new bitmap onto the screen in one go.
\r
4012 * This way we avoid any flickering
\r
4014 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4015 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4016 boardRect.right - boardRect.left,
\r
4017 boardRect.bottom - boardRect.top,
\r
4018 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4019 if(saveDiagFlag) {
\r
4020 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4021 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4023 GetObject(bufferBitmap, sizeof(b), &b);
\r
4024 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4025 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4026 bih.biWidth = b.bmWidth;
\r
4027 bih.biHeight = b.bmHeight;
\r
4029 bih.biBitCount = b.bmBitsPixel;
\r
4030 bih.biCompression = 0;
\r
4031 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4032 bih.biXPelsPerMeter = 0;
\r
4033 bih.biYPelsPerMeter = 0;
\r
4034 bih.biClrUsed = 0;
\r
4035 bih.biClrImportant = 0;
\r
4036 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4037 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4038 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4039 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4041 wb = b.bmWidthBytes;
\r
4043 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4044 int k = ((int*) pData)[i];
\r
4045 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4046 if(j >= 16) break;
\r
4048 if(j >= nrColors) nrColors = j+1;
\r
4050 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4052 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4053 for(w=0; w<(wb>>2); w+=2) {
\r
4054 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4055 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4056 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4057 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4058 pData[p++] = m | j<<4;
\r
4060 while(p&3) pData[p++] = 0;
\r
4063 wb = ((wb+31)>>5)<<2;
\r
4065 // write BITMAPFILEHEADER
\r
4066 fprintf(diagFile, "BM");
\r
4067 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4068 fputDW(diagFile, 0);
\r
4069 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4070 // write BITMAPINFOHEADER
\r
4071 fputDW(diagFile, 40);
\r
4072 fputDW(diagFile, b.bmWidth);
\r
4073 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4074 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4075 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4076 fputDW(diagFile, 0);
\r
4077 fputDW(diagFile, 0);
\r
4078 fputDW(diagFile, 0);
\r
4079 fputDW(diagFile, 0);
\r
4080 fputDW(diagFile, 0);
\r
4081 fputDW(diagFile, 0);
\r
4082 // write color table
\r
4084 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4085 // write bitmap data
\r
4086 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4087 fputc(pData[i], diagFile);
\r
4092 SelectObject(tmphdc, oldBitmap);
\r
4094 /* Massive cleanup */
\r
4095 for (x = 0; x < num_clips; x++)
\r
4096 DeleteObject(clips[x]);
\r
4099 DeleteObject(bufferBitmap);
\r
4102 ReleaseDC(hwndMain, hdc);
\r
4104 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4106 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4108 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4111 /* CopyBoard(lastDrawn, board);*/
\r
4112 lastDrawnHighlight = highlightInfo;
\r
4113 lastDrawnPremove = premoveHighlightInfo;
\r
4114 lastDrawnFlipView = flipView;
\r
4115 lastDrawnValid[nr] = 1;
\r
4118 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4123 saveDiagFlag = 1; diagFile = f;
\r
4124 HDCDrawPosition(NULL, TRUE, NULL);
\r
4132 /*---------------------------------------------------------------------------*\
\r
4133 | CLIENT PAINT PROCEDURE
\r
4134 | This is the main event-handler for the WM_PAINT message.
\r
4136 \*---------------------------------------------------------------------------*/
\r
4138 PaintProc(HWND hwnd)
\r
4144 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4145 if (IsIconic(hwnd)) {
\r
4146 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4148 if (!appData.monoMode) {
\r
4149 SelectPalette(hdc, hPal, FALSE);
\r
4150 RealizePalette(hdc);
\r
4152 HDCDrawPosition(hdc, 1, NULL);
\r
4153 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4154 flipView = !flipView; partnerUp = !partnerUp;
\r
4155 HDCDrawPosition(hdc, 1, NULL);
\r
4156 flipView = !flipView; partnerUp = !partnerUp;
\r
4159 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4160 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4161 ETO_CLIPPED|ETO_OPAQUE,
\r
4162 &messageRect, messageText, strlen(messageText), NULL);
\r
4163 SelectObject(hdc, oldFont);
\r
4164 DisplayBothClocks();
\r
4167 EndPaint(hwnd,&ps);
\r
4175 * If the user selects on a border boundary, return -1; if off the board,
\r
4176 * return -2. Otherwise map the event coordinate to the square.
\r
4177 * The offset boardRect.left or boardRect.top must already have been
\r
4178 * subtracted from x.
\r
4180 int EventToSquare(x, limit)
\r
4185 if (x < lineGap + border)
\r
4187 x -= lineGap + border;
\r
4188 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4190 x /= (squareSize + lineGap);
\r
4202 DropEnable dropEnables[] = {
\r
4203 { 'P', DP_Pawn, N_("Pawn") },
\r
4204 { 'N', DP_Knight, N_("Knight") },
\r
4205 { 'B', DP_Bishop, N_("Bishop") },
\r
4206 { 'R', DP_Rook, N_("Rook") },
\r
4207 { 'Q', DP_Queen, N_("Queen") },
\r
4211 SetupDropMenu(HMENU hmenu)
\r
4213 int i, count, enable;
\r
4215 extern char white_holding[], black_holding[];
\r
4216 char item[MSG_SIZ];
\r
4218 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4219 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4220 dropEnables[i].piece);
\r
4222 while (p && *p++ == dropEnables[i].piece) count++;
\r
4223 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4224 enable = count > 0 || !appData.testLegality
\r
4225 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4226 && !appData.icsActive);
\r
4227 ModifyMenu(hmenu, dropEnables[i].command,
\r
4228 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4229 dropEnables[i].command, item);
\r
4233 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4235 dragInfo.lastpos.x = boardRect.left + x;
\r
4236 dragInfo.lastpos.y = boardRect.top + y;
\r
4237 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4238 dragInfo.from.x = fromX;
\r
4239 dragInfo.from.y = fromY;
\r
4240 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4241 dragInfo.start = dragInfo.from;
\r
4242 SetCapture(hwndMain);
\r
4245 void DragPieceEnd(int x, int y)
\r
4248 dragInfo.start.x = dragInfo.start.y = -1;
\r
4249 dragInfo.from = dragInfo.start;
\r
4250 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4253 void ChangeDragPiece(ChessSquare piece)
\r
4255 dragInfo.piece = piece;
\r
4258 /* Event handler for mouse messages */
\r
4260 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4264 static int recursive = 0;
\r
4266 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4269 if (message == WM_MBUTTONUP) {
\r
4270 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4271 to the middle button: we simulate pressing the left button too!
\r
4273 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4274 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4280 pt.x = LOWORD(lParam);
\r
4281 pt.y = HIWORD(lParam);
\r
4282 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4283 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4284 if (!flipView && y >= 0) {
\r
4285 y = BOARD_HEIGHT - 1 - y;
\r
4287 if (flipView && x >= 0) {
\r
4288 x = BOARD_WIDTH - 1 - x;
\r
4291 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4292 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4294 switch (message) {
\r
4295 case WM_LBUTTONDOWN:
\r
4296 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4297 ClockClick(flipClock); break;
\r
4298 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4299 ClockClick(!flipClock); break;
\r
4301 dragInfo.start.x = dragInfo.start.y = -1;
\r
4302 dragInfo.from = dragInfo.start;
\r
4303 if(fromX == -1 && frozen) { // not sure where this is for
\r
4304 fromX = fromY = -1;
\r
4305 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4308 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4309 DrawPosition(TRUE, NULL);
\r
4312 case WM_LBUTTONUP:
\r
4313 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4314 DrawPosition(TRUE, NULL);
\r
4317 case WM_MOUSEMOVE:
\r
4318 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4319 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4320 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4321 if ((appData.animateDragging || appData.highlightDragging)
\r
4322 && (wParam & MK_LBUTTON)
\r
4323 && dragInfo.from.x >= 0)
\r
4325 BOOL full_repaint = FALSE;
\r
4327 if (appData.animateDragging) {
\r
4328 dragInfo.pos = pt;
\r
4330 if (appData.highlightDragging) {
\r
4331 SetHighlights(fromX, fromY, x, y);
\r
4332 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4333 full_repaint = TRUE;
\r
4337 DrawPosition( full_repaint, NULL);
\r
4339 dragInfo.lastpos = dragInfo.pos;
\r
4343 case WM_MOUSEWHEEL: // [DM]
\r
4344 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4345 /* Mouse Wheel is being rolled forward
\r
4346 * Play moves forward
\r
4348 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4349 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4350 /* Mouse Wheel is being rolled backward
\r
4351 * Play moves backward
\r
4353 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4354 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4358 case WM_MBUTTONUP:
\r
4359 case WM_RBUTTONUP:
\r
4361 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4364 case WM_MBUTTONDOWN:
\r
4365 case WM_RBUTTONDOWN:
\r
4368 fromX = fromY = -1;
\r
4369 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4370 dragInfo.start.x = dragInfo.start.y = -1;
\r
4371 dragInfo.from = dragInfo.start;
\r
4372 dragInfo.lastpos = dragInfo.pos;
\r
4373 if (appData.highlightDragging) {
\r
4374 ClearHighlights();
\r
4377 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4378 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4379 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4380 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4381 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4385 DrawPosition(TRUE, NULL);
\r
4387 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4390 if (message == WM_MBUTTONDOWN) {
\r
4391 buttonCount = 3; /* even if system didn't think so */
\r
4392 if (wParam & MK_SHIFT)
\r
4393 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4395 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4396 } else { /* message == WM_RBUTTONDOWN */
\r
4397 /* Just have one menu, on the right button. Windows users don't
\r
4398 think to try the middle one, and sometimes other software steals
\r
4399 it, or it doesn't really exist. */
\r
4400 if(gameInfo.variant != VariantShogi)
\r
4401 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4403 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4407 SetCapture(hwndMain);
\r
4410 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4411 SetupDropMenu(hmenu);
\r
4412 MenuPopup(hwnd, pt, hmenu, -1);
\r
4422 /* Preprocess messages for buttons in main window */
\r
4424 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4426 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4429 for (i=0; i<N_BUTTONS; i++) {
\r
4430 if (buttonDesc[i].id == id) break;
\r
4432 if (i == N_BUTTONS) return 0;
\r
4433 switch (message) {
\r
4438 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4439 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4446 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4449 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4450 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4451 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4452 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4454 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4456 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4457 TypeInEvent((char)wParam);
\r
4463 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4466 /* Process messages for Promotion dialog box */
\r
4468 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4472 switch (message) {
\r
4473 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4474 /* Center the dialog over the application window */
\r
4475 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4476 Translate(hDlg, DLG_PromotionKing);
\r
4477 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4478 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4479 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4480 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4481 SW_SHOW : SW_HIDE);
\r
4482 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4483 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4484 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4485 PieceToChar(WhiteAngel) != '~') ||
\r
4486 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4487 PieceToChar(BlackAngel) != '~') ) ?
\r
4488 SW_SHOW : SW_HIDE);
\r
4489 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4490 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4491 PieceToChar(WhiteMarshall) != '~') ||
\r
4492 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4493 PieceToChar(BlackMarshall) != '~') ) ?
\r
4494 SW_SHOW : SW_HIDE);
\r
4495 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4496 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4497 gameInfo.variant != VariantShogi ?
\r
4498 SW_SHOW : SW_HIDE);
\r
4499 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4500 gameInfo.variant != VariantShogi ?
\r
4501 SW_SHOW : SW_HIDE);
\r
4502 if(gameInfo.variant == VariantShogi) {
\r
4503 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4504 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4505 SetWindowText(hDlg, "Promote?");
\r
4507 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4508 gameInfo.variant == VariantSuper ?
\r
4509 SW_SHOW : SW_HIDE);
\r
4512 case WM_COMMAND: /* message: received a command */
\r
4513 switch (LOWORD(wParam)) {
\r
4515 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4516 ClearHighlights();
\r
4517 DrawPosition(FALSE, NULL);
\r
4520 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4523 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4526 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4527 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4530 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4531 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4533 case PB_Chancellor:
\r
4534 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4536 case PB_Archbishop:
\r
4537 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4540 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4545 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4546 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4547 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4548 fromX = fromY = -1;
\r
4549 if (!appData.highlightLastMove) {
\r
4550 ClearHighlights();
\r
4551 DrawPosition(FALSE, NULL);
\r
4558 /* Pop up promotion dialog */
\r
4560 PromotionPopup(HWND hwnd)
\r
4564 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4565 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4566 hwnd, (DLGPROC)lpProc);
\r
4567 FreeProcInstance(lpProc);
\r
4573 DrawPosition(TRUE, NULL);
\r
4574 PromotionPopup(hwndMain);
\r
4578 LoadGameDialog(HWND hwnd, char* title)
\r
4582 char fileTitle[MSG_SIZ];
\r
4583 f = OpenFileDialog(hwnd, "rb", "",
\r
4584 appData.oldSaveStyle ? "gam" : "pgn",
\r
4586 title, &number, fileTitle, NULL);
\r
4588 cmailMsgLoaded = FALSE;
\r
4589 if (number == 0) {
\r
4590 int error = GameListBuild(f);
\r
4592 DisplayError(_("Cannot build game list"), error);
\r
4593 } else if (!ListEmpty(&gameList) &&
\r
4594 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4595 GameListPopUp(f, fileTitle);
\r
4598 GameListDestroy();
\r
4601 LoadGame(f, number, fileTitle, FALSE);
\r
4605 int get_term_width()
\r
4610 HFONT hfont, hold_font;
\r
4615 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4619 // get the text metrics
\r
4620 hdc = GetDC(hText);
\r
4621 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4622 if (consoleCF.dwEffects & CFE_BOLD)
\r
4623 lf.lfWeight = FW_BOLD;
\r
4624 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4625 lf.lfItalic = TRUE;
\r
4626 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4627 lf.lfStrikeOut = TRUE;
\r
4628 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4629 lf.lfUnderline = TRUE;
\r
4630 hfont = CreateFontIndirect(&lf);
\r
4631 hold_font = SelectObject(hdc, hfont);
\r
4632 GetTextMetrics(hdc, &tm);
\r
4633 SelectObject(hdc, hold_font);
\r
4634 DeleteObject(hfont);
\r
4635 ReleaseDC(hText, hdc);
\r
4637 // get the rectangle
\r
4638 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4640 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4643 void UpdateICSWidth(HWND hText)
\r
4645 LONG old_width, new_width;
\r
4647 new_width = get_term_width(hText, FALSE);
\r
4648 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4649 if (new_width != old_width)
\r
4651 ics_update_width(new_width);
\r
4652 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4657 ChangedConsoleFont()
\r
4660 CHARRANGE tmpsel, sel;
\r
4661 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4662 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4663 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4666 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4667 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4668 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4669 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4670 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4671 * size. This was undocumented in the version of MSVC++ that I had
\r
4672 * when I wrote the code, but is apparently documented now.
\r
4674 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4675 cfmt.bCharSet = f->lf.lfCharSet;
\r
4676 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4677 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4678 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4679 /* Why are the following seemingly needed too? */
\r
4680 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4681 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4682 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4684 tmpsel.cpMax = -1; /*999999?*/
\r
4685 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4686 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4687 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4688 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4690 paraf.cbSize = sizeof(paraf);
\r
4691 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4692 paraf.dxStartIndent = 0;
\r
4693 paraf.dxOffset = WRAP_INDENT;
\r
4694 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4695 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4696 UpdateICSWidth(hText);
\r
4699 /*---------------------------------------------------------------------------*\
\r
4701 * Window Proc for main window
\r
4703 \*---------------------------------------------------------------------------*/
\r
4705 /* Process messages for main window, etc. */
\r
4707 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4710 int wmId, wmEvent;
\r
4714 char fileTitle[MSG_SIZ];
\r
4715 static SnapData sd;
\r
4716 static int peek=0;
\r
4718 switch (message) {
\r
4720 case WM_PAINT: /* message: repaint portion of window */
\r
4724 case WM_ERASEBKGND:
\r
4725 if (IsIconic(hwnd)) {
\r
4726 /* Cheat; change the message */
\r
4727 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4729 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4733 case WM_LBUTTONDOWN:
\r
4734 case WM_MBUTTONDOWN:
\r
4735 case WM_RBUTTONDOWN:
\r
4736 case WM_LBUTTONUP:
\r
4737 case WM_MBUTTONUP:
\r
4738 case WM_RBUTTONUP:
\r
4739 case WM_MOUSEMOVE:
\r
4740 case WM_MOUSEWHEEL:
\r
4741 MouseEvent(hwnd, message, wParam, lParam);
\r
4745 if((char)wParam == '\b') {
\r
4746 ForwardEvent(); peek = 0;
\r
4749 JAWS_KBUP_NAVIGATION
\r
4754 if((char)wParam == '\b') {
\r
4755 if(!peek) BackwardEvent(), peek = 1;
\r
4758 JAWS_KBDOWN_NAVIGATION
\r
4764 JAWS_ALT_INTERCEPT
\r
4766 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4767 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4768 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4769 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4771 SendMessage(h, message, wParam, lParam);
\r
4772 } else if(lParam != KF_REPEAT) {
\r
4773 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4774 TypeInEvent((char)wParam);
\r
4775 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4776 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4781 case WM_PALETTECHANGED:
\r
4782 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4784 HDC hdc = GetDC(hwndMain);
\r
4785 SelectPalette(hdc, hPal, TRUE);
\r
4786 nnew = RealizePalette(hdc);
\r
4788 paletteChanged = TRUE;
\r
4789 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4791 ReleaseDC(hwnd, hdc);
\r
4795 case WM_QUERYNEWPALETTE:
\r
4796 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4798 HDC hdc = GetDC(hwndMain);
\r
4799 paletteChanged = FALSE;
\r
4800 SelectPalette(hdc, hPal, FALSE);
\r
4801 nnew = RealizePalette(hdc);
\r
4803 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4805 ReleaseDC(hwnd, hdc);
\r
4810 case WM_COMMAND: /* message: command from application menu */
\r
4811 wmId = LOWORD(wParam);
\r
4812 wmEvent = HIWORD(wParam);
\r
4817 SAY("new game enter a move to play against the computer with white");
\r
4820 case IDM_NewGameFRC:
\r
4821 if( NewGameFRC() == 0 ) {
\r
4826 case IDM_NewVariant:
\r
4827 NewVariantPopup(hwnd);
\r
4830 case IDM_LoadGame:
\r
4831 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4834 case IDM_LoadNextGame:
\r
4838 case IDM_LoadPrevGame:
\r
4842 case IDM_ReloadGame:
\r
4846 case IDM_LoadPosition:
\r
4847 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4848 Reset(FALSE, TRUE);
\r
4851 f = OpenFileDialog(hwnd, "rb", "",
\r
4852 appData.oldSaveStyle ? "pos" : "fen",
\r
4854 _("Load Position from File"), &number, fileTitle, NULL);
\r
4856 LoadPosition(f, number, fileTitle);
\r
4860 case IDM_LoadNextPosition:
\r
4861 ReloadPosition(1);
\r
4864 case IDM_LoadPrevPosition:
\r
4865 ReloadPosition(-1);
\r
4868 case IDM_ReloadPosition:
\r
4869 ReloadPosition(0);
\r
4872 case IDM_SaveGame:
\r
4873 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4874 f = OpenFileDialog(hwnd, "a", defName,
\r
4875 appData.oldSaveStyle ? "gam" : "pgn",
\r
4877 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4879 SaveGame(f, 0, "");
\r
4883 case IDM_SavePosition:
\r
4884 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4885 f = OpenFileDialog(hwnd, "a", defName,
\r
4886 appData.oldSaveStyle ? "pos" : "fen",
\r
4888 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4890 SavePosition(f, 0, "");
\r
4894 case IDM_SaveDiagram:
\r
4895 defName = "diagram";
\r
4896 f = OpenFileDialog(hwnd, "wb", defName,
\r
4899 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4905 case IDM_CreateBook:
\r
4906 CreateBookEvent();
\r
4909 case IDM_CopyGame:
\r
4910 CopyGameToClipboard();
\r
4913 case IDM_PasteGame:
\r
4914 PasteGameFromClipboard();
\r
4917 case IDM_CopyGameListToClipboard:
\r
4918 CopyGameListToClipboard();
\r
4921 /* [AS] Autodetect FEN or PGN data */
\r
4922 case IDM_PasteAny:
\r
4923 PasteGameOrFENFromClipboard();
\r
4926 /* [AS] Move history */
\r
4927 case IDM_ShowMoveHistory:
\r
4928 if( MoveHistoryIsUp() ) {
\r
4929 MoveHistoryPopDown();
\r
4932 MoveHistoryPopUp();
\r
4936 /* [AS] Eval graph */
\r
4937 case IDM_ShowEvalGraph:
\r
4938 if( EvalGraphIsUp() ) {
\r
4939 EvalGraphPopDown();
\r
4943 SetFocus(hwndMain);
\r
4947 /* [AS] Engine output */
\r
4948 case IDM_ShowEngineOutput:
\r
4949 if( EngineOutputIsUp() ) {
\r
4950 EngineOutputPopDown();
\r
4953 EngineOutputPopUp();
\r
4957 /* [AS] User adjudication */
\r
4958 case IDM_UserAdjudication_White:
\r
4959 UserAdjudicationEvent( +1 );
\r
4962 case IDM_UserAdjudication_Black:
\r
4963 UserAdjudicationEvent( -1 );
\r
4966 case IDM_UserAdjudication_Draw:
\r
4967 UserAdjudicationEvent( 0 );
\r
4970 /* [AS] Game list options dialog */
\r
4971 case IDM_GameListOptions:
\r
4972 GameListOptions();
\r
4979 case IDM_CopyPosition:
\r
4980 CopyFENToClipboard();
\r
4983 case IDM_PastePosition:
\r
4984 PasteFENFromClipboard();
\r
4987 case IDM_MailMove:
\r
4991 case IDM_ReloadCMailMsg:
\r
4992 Reset(TRUE, TRUE);
\r
4993 ReloadCmailMsgEvent(FALSE);
\r
4996 case IDM_Minimize:
\r
4997 ShowWindow(hwnd, SW_MINIMIZE);
\r
5004 case IDM_MachineWhite:
\r
5005 MachineWhiteEvent();
\r
5007 * refresh the tags dialog only if it's visible
\r
5009 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5011 tags = PGNTags(&gameInfo);
\r
5012 TagsPopUp(tags, CmailMsg());
\r
5015 SAY("computer starts playing white");
\r
5018 case IDM_MachineBlack:
\r
5019 MachineBlackEvent();
\r
5021 * refresh the tags dialog only if it's visible
\r
5023 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5025 tags = PGNTags(&gameInfo);
\r
5026 TagsPopUp(tags, CmailMsg());
\r
5029 SAY("computer starts playing black");
\r
5032 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5033 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5036 case IDM_TwoMachines:
\r
5037 TwoMachinesEvent();
\r
5039 * refresh the tags dialog only if it's visible
\r
5041 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5043 tags = PGNTags(&gameInfo);
\r
5044 TagsPopUp(tags, CmailMsg());
\r
5047 SAY("computer starts playing both sides");
\r
5050 case IDM_AnalysisMode:
\r
5051 if(AnalyzeModeEvent()) {
\r
5052 SAY("analyzing current position");
\r
5056 case IDM_AnalyzeFile:
\r
5057 AnalyzeFileEvent();
\r
5060 case IDM_IcsClient:
\r
5064 case IDM_EditGame:
\r
5065 case IDM_EditGame2:
\r
5070 case IDM_EditPosition:
\r
5071 case IDM_EditPosition2:
\r
5072 EditPositionEvent();
\r
5073 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5076 case IDM_Training:
\r
5080 case IDM_ShowGameList:
\r
5081 ShowGameListProc();
\r
5084 case IDM_EditProgs1:
\r
5085 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5088 case IDM_LoadProg1:
\r
5089 LoadEnginePopUp(hwndMain, 0);
\r
5092 case IDM_LoadProg2:
\r
5093 LoadEnginePopUp(hwndMain, 1);
\r
5096 case IDM_EditServers:
\r
5097 EditTagsPopUp(icsNames, &icsNames);
\r
5100 case IDM_EditTags:
\r
5105 case IDM_EditBook:
\r
5109 case IDM_EditComment:
\r
5111 if (commentUp && editComment) {
\r
5114 EditCommentEvent();
\r
5134 case IDM_CallFlag:
\r
5154 case IDM_StopObserving:
\r
5155 StopObservingEvent();
\r
5158 case IDM_StopExamining:
\r
5159 StopExaminingEvent();
\r
5163 UploadGameEvent();
\r
5166 case IDM_TypeInMove:
\r
5167 TypeInEvent('\000');
\r
5170 case IDM_TypeInName:
\r
5171 PopUpNameDialog('\000');
\r
5174 case IDM_Backward:
\r
5176 SetFocus(hwndMain);
\r
5183 SetFocus(hwndMain);
\r
5188 SetFocus(hwndMain);
\r
5193 SetFocus(hwndMain);
\r
5196 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5197 case OPT_GameListPrev:
\r
5198 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5202 RevertEvent(FALSE);
\r
5205 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5206 RevertEvent(TRUE);
\r
5209 case IDM_TruncateGame:
\r
5210 TruncateGameEvent();
\r
5217 case IDM_RetractMove:
\r
5218 RetractMoveEvent();
\r
5221 case IDM_FlipView:
\r
5222 flipView = !flipView;
\r
5223 DrawPosition(FALSE, NULL);
\r
5226 case IDM_FlipClock:
\r
5227 flipClock = !flipClock;
\r
5228 DisplayBothClocks();
\r
5232 case IDM_MuteSounds:
\r
5233 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5234 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5235 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5238 case IDM_GeneralOptions:
\r
5239 GeneralOptionsPopup(hwnd);
\r
5240 DrawPosition(TRUE, NULL);
\r
5243 case IDM_BoardOptions:
\r
5244 BoardOptionsPopup(hwnd);
\r
5247 case IDM_ThemeOptions:
\r
5248 ThemeOptionsPopup(hwnd);
\r
5251 case IDM_EnginePlayOptions:
\r
5252 EnginePlayOptionsPopup(hwnd);
\r
5255 case IDM_Engine1Options:
\r
5256 EngineOptionsPopup(hwnd, &first);
\r
5259 case IDM_Engine2Options:
\r
5261 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5262 EngineOptionsPopup(hwnd, &second);
\r
5265 case IDM_OptionsUCI:
\r
5266 UciOptionsPopup(hwnd);
\r
5270 TourneyPopup(hwnd);
\r
5273 case IDM_IcsOptions:
\r
5274 IcsOptionsPopup(hwnd);
\r
5278 FontsOptionsPopup(hwnd);
\r
5282 SoundOptionsPopup(hwnd);
\r
5285 case IDM_CommPort:
\r
5286 CommPortOptionsPopup(hwnd);
\r
5289 case IDM_LoadOptions:
\r
5290 LoadOptionsPopup(hwnd);
\r
5293 case IDM_SaveOptions:
\r
5294 SaveOptionsPopup(hwnd);
\r
5297 case IDM_TimeControl:
\r
5298 TimeControlOptionsPopup(hwnd);
\r
5301 case IDM_SaveSettings:
\r
5302 SaveSettings(settingsFileName);
\r
5305 case IDM_SaveSettingsOnExit:
\r
5306 saveSettingsOnExit = !saveSettingsOnExit;
\r
5307 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5308 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5309 MF_CHECKED : MF_UNCHECKED));
\r
5320 case IDM_AboutGame:
\r
5325 appData.debugMode = !appData.debugMode;
\r
5326 if (appData.debugMode) {
\r
5327 char dir[MSG_SIZ];
\r
5328 GetCurrentDirectory(MSG_SIZ, dir);
\r
5329 SetCurrentDirectory(installDir);
\r
5330 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5331 SetCurrentDirectory(dir);
\r
5332 setbuf(debugFP, NULL);
\r
5339 case IDM_HELPCONTENTS:
\r
5340 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5341 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5342 MessageBox (GetFocus(),
\r
5343 _("Unable to activate help"),
\r
5344 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5348 case IDM_HELPSEARCH:
\r
5349 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5350 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5351 MessageBox (GetFocus(),
\r
5352 _("Unable to activate help"),
\r
5353 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5357 case IDM_HELPHELP:
\r
5358 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5359 MessageBox (GetFocus(),
\r
5360 _("Unable to activate help"),
\r
5361 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5366 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5368 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5369 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5370 FreeProcInstance(lpProc);
\r
5373 case IDM_DirectCommand1:
\r
5374 AskQuestionEvent(_("Direct Command"),
\r
5375 _("Send to chess program:"), "", "1");
\r
5377 case IDM_DirectCommand2:
\r
5378 AskQuestionEvent(_("Direct Command"),
\r
5379 _("Send to second chess program:"), "", "2");
\r
5382 case EP_WhitePawn:
\r
5383 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5387 case EP_WhiteKnight:
\r
5388 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5392 case EP_WhiteBishop:
\r
5393 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5394 fromX = fromY = -1;
\r
5397 case EP_WhiteRook:
\r
5398 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5402 case EP_WhiteQueen:
\r
5403 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5404 fromX = fromY = -1;
\r
5407 case EP_WhiteFerz:
\r
5408 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5409 fromX = fromY = -1;
\r
5412 case EP_WhiteWazir:
\r
5413 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5414 fromX = fromY = -1;
\r
5417 case EP_WhiteAlfil:
\r
5418 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5419 fromX = fromY = -1;
\r
5422 case EP_WhiteCannon:
\r
5423 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5424 fromX = fromY = -1;
\r
5427 case EP_WhiteCardinal:
\r
5428 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5429 fromX = fromY = -1;
\r
5432 case EP_WhiteMarshall:
\r
5433 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5434 fromX = fromY = -1;
\r
5437 case EP_WhiteKing:
\r
5438 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5439 fromX = fromY = -1;
\r
5442 case EP_BlackPawn:
\r
5443 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5444 fromX = fromY = -1;
\r
5447 case EP_BlackKnight:
\r
5448 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5449 fromX = fromY = -1;
\r
5452 case EP_BlackBishop:
\r
5453 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5454 fromX = fromY = -1;
\r
5457 case EP_BlackRook:
\r
5458 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5459 fromX = fromY = -1;
\r
5462 case EP_BlackQueen:
\r
5463 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5464 fromX = fromY = -1;
\r
5467 case EP_BlackFerz:
\r
5468 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5469 fromX = fromY = -1;
\r
5472 case EP_BlackWazir:
\r
5473 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5474 fromX = fromY = -1;
\r
5477 case EP_BlackAlfil:
\r
5478 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5479 fromX = fromY = -1;
\r
5482 case EP_BlackCannon:
\r
5483 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5484 fromX = fromY = -1;
\r
5487 case EP_BlackCardinal:
\r
5488 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5489 fromX = fromY = -1;
\r
5492 case EP_BlackMarshall:
\r
5493 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5494 fromX = fromY = -1;
\r
5497 case EP_BlackKing:
\r
5498 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5499 fromX = fromY = -1;
\r
5502 case EP_EmptySquare:
\r
5503 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5504 fromX = fromY = -1;
\r
5507 case EP_ClearBoard:
\r
5508 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5509 fromX = fromY = -1;
\r
5513 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5514 fromX = fromY = -1;
\r
5518 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5519 fromX = fromY = -1;
\r
5523 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5524 fromX = fromY = -1;
\r
5528 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5529 fromX = fromY = -1;
\r
5533 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5534 fromX = fromY = -1;
\r
5538 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5539 fromX = fromY = -1;
\r
5543 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5544 fromX = fromY = -1;
\r
5548 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5549 fromX = fromY = -1;
\r
5553 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5554 fromX = fromY = -1;
\r
5558 barbaric = 0; appData.language = "";
\r
5559 TranslateMenus(0);
\r
5560 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5561 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5562 lastChecked = wmId;
\r
5566 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5567 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5569 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5570 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5571 TranslateMenus(0);
\r
5572 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5573 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5574 lastChecked = wmId;
\r
5577 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5583 case CLOCK_TIMER_ID:
\r
5584 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5585 clockTimerEvent = 0;
\r
5586 DecrementClocks(); /* call into back end */
\r
5588 case LOAD_GAME_TIMER_ID:
\r
5589 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5590 loadGameTimerEvent = 0;
\r
5591 AutoPlayGameLoop(); /* call into back end */
\r
5593 case ANALYSIS_TIMER_ID:
\r
5594 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5595 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5596 AnalysisPeriodicEvent(0);
\r
5598 KillTimer(hwnd, analysisTimerEvent);
\r
5599 analysisTimerEvent = 0;
\r
5602 case DELAYED_TIMER_ID:
\r
5603 KillTimer(hwnd, delayedTimerEvent);
\r
5604 delayedTimerEvent = 0;
\r
5605 delayedTimerCallback();
\r
5610 case WM_USER_Input:
\r
5611 InputEvent(hwnd, message, wParam, lParam);
\r
5614 /* [AS] Also move "attached" child windows */
\r
5615 case WM_WINDOWPOSCHANGING:
\r
5617 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5618 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5620 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5621 /* Window is moving */
\r
5624 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5625 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5626 rcMain.right = wpMain.x + wpMain.width;
\r
5627 rcMain.top = wpMain.y;
\r
5628 rcMain.bottom = wpMain.y + wpMain.height;
\r
5630 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5631 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5632 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5633 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5634 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5635 wpMain.x = lpwp->x;
\r
5636 wpMain.y = lpwp->y;
\r
5641 /* [AS] Snapping */
\r
5642 case WM_ENTERSIZEMOVE:
\r
5643 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5644 if (hwnd == hwndMain) {
\r
5645 doingSizing = TRUE;
\r
5648 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5652 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5653 if (hwnd == hwndMain) {
\r
5654 lastSizing = wParam;
\r
5659 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5660 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5662 case WM_EXITSIZEMOVE:
\r
5663 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5664 if (hwnd == hwndMain) {
\r
5666 doingSizing = FALSE;
\r
5667 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5668 GetClientRect(hwnd, &client);
\r
5669 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5671 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5673 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5676 case WM_DESTROY: /* message: window being destroyed */
\r
5677 PostQuitMessage(0);
\r
5681 if (hwnd == hwndMain) {
\r
5686 default: /* Passes it on if unprocessed */
\r
5687 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5692 /*---------------------------------------------------------------------------*\
\r
5694 * Misc utility routines
\r
5696 \*---------------------------------------------------------------------------*/
\r
5699 * Decent random number generator, at least not as bad as Windows
\r
5700 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5702 unsigned int randstate;
\r
5707 randstate = randstate * 1664525 + 1013904223;
\r
5708 return (int) randstate & 0x7fffffff;
\r
5712 mysrandom(unsigned int seed)
\r
5719 * returns TRUE if user selects a different color, FALSE otherwise
\r
5723 ChangeColor(HWND hwnd, COLORREF *which)
\r
5725 static BOOL firstTime = TRUE;
\r
5726 static DWORD customColors[16];
\r
5728 COLORREF newcolor;
\r
5733 /* Make initial colors in use available as custom colors */
\r
5734 /* Should we put the compiled-in defaults here instead? */
\r
5736 customColors[i++] = lightSquareColor & 0xffffff;
\r
5737 customColors[i++] = darkSquareColor & 0xffffff;
\r
5738 customColors[i++] = whitePieceColor & 0xffffff;
\r
5739 customColors[i++] = blackPieceColor & 0xffffff;
\r
5740 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5741 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5743 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5744 customColors[i++] = textAttribs[ccl].color;
\r
5746 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5747 firstTime = FALSE;
\r
5750 cc.lStructSize = sizeof(cc);
\r
5751 cc.hwndOwner = hwnd;
\r
5752 cc.hInstance = NULL;
\r
5753 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5754 cc.lpCustColors = (LPDWORD) customColors;
\r
5755 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5757 if (!ChooseColor(&cc)) return FALSE;
\r
5759 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5760 if (newcolor == *which) return FALSE;
\r
5761 *which = newcolor;
\r
5765 InitDrawingColors();
\r
5766 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5771 MyLoadSound(MySound *ms)
\r
5777 if (ms->data && ms->flag) free(ms->data);
\r
5780 switch (ms->name[0]) {
\r
5786 /* System sound from Control Panel. Don't preload here. */
\r
5790 if (ms->name[1] == NULLCHAR) {
\r
5791 /* "!" alone = silence */
\r
5794 /* Builtin wave resource. Error if not found. */
\r
5795 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5796 if (h == NULL) break;
\r
5797 ms->data = (void *)LoadResource(hInst, h);
\r
5798 ms->flag = 0; // not maloced, so cannot be freed!
\r
5799 if (h == NULL) break;
\r
5804 /* .wav file. Error if not found. */
\r
5805 f = fopen(ms->name, "rb");
\r
5806 if (f == NULL) break;
\r
5807 if (fstat(fileno(f), &st) < 0) break;
\r
5808 ms->data = malloc(st.st_size);
\r
5810 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5816 char buf[MSG_SIZ];
\r
5817 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5818 DisplayError(buf, GetLastError());
\r
5824 MyPlaySound(MySound *ms)
\r
5826 BOOLEAN ok = FALSE;
\r
5828 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5829 switch (ms->name[0]) {
\r
5831 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5836 /* System sound from Control Panel (deprecated feature).
\r
5837 "$" alone or an unset sound name gets default beep (still in use). */
\r
5838 if (ms->name[1]) {
\r
5839 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5841 if (!ok) ok = MessageBeep(MB_OK);
\r
5844 /* Builtin wave resource, or "!" alone for silence */
\r
5845 if (ms->name[1]) {
\r
5846 if (ms->data == NULL) return FALSE;
\r
5847 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5853 /* .wav file. Error if not found. */
\r
5854 if (ms->data == NULL) return FALSE;
\r
5855 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5858 /* Don't print an error: this can happen innocently if the sound driver
\r
5859 is busy; for instance, if another instance of WinBoard is playing
\r
5860 a sound at about the same time. */
\r
5866 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5869 OPENFILENAME *ofn;
\r
5870 static UINT *number; /* gross that this is static */
\r
5872 switch (message) {
\r
5873 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5874 /* Center the dialog over the application window */
\r
5875 ofn = (OPENFILENAME *) lParam;
\r
5876 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5877 number = (UINT *) ofn->lCustData;
\r
5878 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5882 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5883 Translate(hDlg, 1536);
\r
5884 return FALSE; /* Allow for further processing */
\r
5887 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5888 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5890 return FALSE; /* Allow for further processing */
\r
5896 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5898 static UINT *number;
\r
5899 OPENFILENAME *ofname;
\r
5902 case WM_INITDIALOG:
\r
5903 Translate(hdlg, DLG_IndexNumber);
\r
5904 ofname = (OPENFILENAME *)lParam;
\r
5905 number = (UINT *)(ofname->lCustData);
\r
5908 ofnot = (OFNOTIFY *)lParam;
\r
5909 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5910 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5919 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5920 char *nameFilt, char *dlgTitle, UINT *number,
\r
5921 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5923 OPENFILENAME openFileName;
\r
5924 char buf1[MSG_SIZ];
\r
5927 if (fileName == NULL) fileName = buf1;
\r
5928 if (defName == NULL) {
\r
5929 safeStrCpy(fileName, "*.", 3 );
\r
5930 strcat(fileName, defExt);
\r
5932 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5934 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5935 if (number) *number = 0;
\r
5937 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5938 openFileName.hwndOwner = hwnd;
\r
5939 openFileName.hInstance = (HANDLE) hInst;
\r
5940 openFileName.lpstrFilter = nameFilt;
\r
5941 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5942 openFileName.nMaxCustFilter = 0L;
\r
5943 openFileName.nFilterIndex = 1L;
\r
5944 openFileName.lpstrFile = fileName;
\r
5945 openFileName.nMaxFile = MSG_SIZ;
\r
5946 openFileName.lpstrFileTitle = fileTitle;
\r
5947 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5948 openFileName.lpstrInitialDir = NULL;
\r
5949 openFileName.lpstrTitle = dlgTitle;
\r
5950 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5951 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5952 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5953 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5954 openFileName.nFileOffset = 0;
\r
5955 openFileName.nFileExtension = 0;
\r
5956 openFileName.lpstrDefExt = defExt;
\r
5957 openFileName.lCustData = (LONG) number;
\r
5958 openFileName.lpfnHook = oldDialog ?
\r
5959 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5960 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5962 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5963 GetOpenFileName(&openFileName)) {
\r
5964 /* open the file */
\r
5965 f = fopen(openFileName.lpstrFile, write);
\r
5967 MessageBox(hwnd, _("File open failed"), NULL,
\r
5968 MB_OK|MB_ICONEXCLAMATION);
\r
5972 int err = CommDlgExtendedError();
\r
5973 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5982 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5984 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5987 * Get the first pop-up menu in the menu template. This is the
\r
5988 * menu that TrackPopupMenu displays.
\r
5990 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5991 TranslateOneMenu(10, hmenuTrackPopup);
\r
5993 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5996 * TrackPopup uses screen coordinates, so convert the
\r
5997 * coordinates of the mouse click to screen coordinates.
\r
5999 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6001 /* Draw and track the floating pop-up menu. */
\r
6002 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6003 pt.x, pt.y, 0, hwnd, NULL);
\r
6005 /* Destroy the menu.*/
\r
6006 DestroyMenu(hmenu);
\r
6011 int sizeX, sizeY, newSizeX, newSizeY;
\r
6013 } ResizeEditPlusButtonsClosure;
\r
6016 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6018 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6022 if (hChild == cl->hText) return TRUE;
\r
6023 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6024 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6025 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6026 ScreenToClient(cl->hDlg, &pt);
\r
6027 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6028 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6032 /* Resize a dialog that has a (rich) edit field filling most of
\r
6033 the top, with a row of buttons below */
\r
6035 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6038 int newTextHeight, newTextWidth;
\r
6039 ResizeEditPlusButtonsClosure cl;
\r
6041 /*if (IsIconic(hDlg)) return;*/
\r
6042 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6044 cl.hdwp = BeginDeferWindowPos(8);
\r
6046 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6047 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6048 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6049 if (newTextHeight < 0) {
\r
6050 newSizeY += -newTextHeight;
\r
6051 newTextHeight = 0;
\r
6053 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6054 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6060 cl.newSizeX = newSizeX;
\r
6061 cl.newSizeY = newSizeY;
\r
6062 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6064 EndDeferWindowPos(cl.hdwp);
\r
6067 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6069 RECT rChild, rParent;
\r
6070 int wChild, hChild, wParent, hParent;
\r
6071 int wScreen, hScreen, xNew, yNew;
\r
6074 /* Get the Height and Width of the child window */
\r
6075 GetWindowRect (hwndChild, &rChild);
\r
6076 wChild = rChild.right - rChild.left;
\r
6077 hChild = rChild.bottom - rChild.top;
\r
6079 /* Get the Height and Width of the parent window */
\r
6080 GetWindowRect (hwndParent, &rParent);
\r
6081 wParent = rParent.right - rParent.left;
\r
6082 hParent = rParent.bottom - rParent.top;
\r
6084 /* Get the display limits */
\r
6085 hdc = GetDC (hwndChild);
\r
6086 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6087 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6088 ReleaseDC(hwndChild, hdc);
\r
6090 /* Calculate new X position, then adjust for screen */
\r
6091 xNew = rParent.left + ((wParent - wChild) /2);
\r
6094 } else if ((xNew+wChild) > wScreen) {
\r
6095 xNew = wScreen - wChild;
\r
6098 /* Calculate new Y position, then adjust for screen */
\r
6100 yNew = rParent.top + ((hParent - hChild) /2);
\r
6103 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6108 } else if ((yNew+hChild) > hScreen) {
\r
6109 yNew = hScreen - hChild;
\r
6112 /* Set it, and return */
\r
6113 return SetWindowPos (hwndChild, NULL,
\r
6114 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6117 /* Center one window over another */
\r
6118 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6120 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6123 /*---------------------------------------------------------------------------*\
\r
6125 * Startup Dialog functions
\r
6127 \*---------------------------------------------------------------------------*/
\r
6129 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6131 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6133 while (*cd != NULL) {
\r
6134 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6140 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6142 char buf1[MAX_ARG_LEN];
\r
6145 if (str[0] == '@') {
\r
6146 FILE* f = fopen(str + 1, "r");
\r
6148 DisplayFatalError(str + 1, errno, 2);
\r
6151 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6153 buf1[len] = NULLCHAR;
\r
6157 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6160 char buf[MSG_SIZ];
\r
6161 char *end = strchr(str, '\n');
\r
6162 if (end == NULL) return;
\r
6163 memcpy(buf, str, end - str);
\r
6164 buf[end - str] = NULLCHAR;
\r
6165 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6171 SetStartupDialogEnables(HWND hDlg)
\r
6173 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6174 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6175 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6176 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6177 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6178 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6179 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6180 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6181 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6182 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6183 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6184 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6185 IsDlgButtonChecked(hDlg, OPT_View));
\r
6189 QuoteForFilename(char *filename)
\r
6191 int dquote, space;
\r
6192 dquote = strchr(filename, '"') != NULL;
\r
6193 space = strchr(filename, ' ') != NULL;
\r
6194 if (dquote || space) {
\r
6206 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6208 char buf[MSG_SIZ];
\r
6211 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6212 q = QuoteForFilename(nthcp);
\r
6213 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6214 if (*nthdir != NULLCHAR) {
\r
6215 q = QuoteForFilename(nthdir);
\r
6216 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6218 if (*nthcp == NULLCHAR) {
\r
6219 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6220 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6221 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6222 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6227 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6229 char buf[MSG_SIZ];
\r
6233 switch (message) {
\r
6234 case WM_INITDIALOG:
\r
6235 /* Center the dialog */
\r
6236 CenterWindow (hDlg, GetDesktopWindow());
\r
6237 Translate(hDlg, DLG_Startup);
\r
6238 /* Initialize the dialog items */
\r
6239 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6240 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6241 firstChessProgramNames);
\r
6242 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6243 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6244 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6245 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6246 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6247 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6248 if (*appData.icsHelper != NULLCHAR) {
\r
6249 char *q = QuoteForFilename(appData.icsHelper);
\r
6250 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6252 if (*appData.icsHost == NULLCHAR) {
\r
6253 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6254 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6255 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6256 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6257 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6260 if (appData.icsActive) {
\r
6261 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6263 else if (appData.noChessProgram) {
\r
6264 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6267 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6270 SetStartupDialogEnables(hDlg);
\r
6274 switch (LOWORD(wParam)) {
\r
6276 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6277 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6278 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6280 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6281 ParseArgs(StringGet, &p);
\r
6282 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6283 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6285 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6286 ParseArgs(StringGet, &p);
\r
6287 SwapEngines(singleList); // ... and then make it 'second'
\r
6288 appData.noChessProgram = FALSE;
\r
6289 appData.icsActive = FALSE;
\r
6290 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6291 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6292 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6294 ParseArgs(StringGet, &p);
\r
6295 if (appData.zippyPlay) {
\r
6296 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6297 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6299 ParseArgs(StringGet, &p);
\r
6301 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6302 appData.noChessProgram = TRUE;
\r
6303 appData.icsActive = FALSE;
\r
6305 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6306 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6309 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6310 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6312 ParseArgs(StringGet, &p);
\r
6314 EndDialog(hDlg, TRUE);
\r
6321 case IDM_HELPCONTENTS:
\r
6322 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6323 MessageBox (GetFocus(),
\r
6324 _("Unable to activate help"),
\r
6325 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6330 SetStartupDialogEnables(hDlg);
\r
6338 /*---------------------------------------------------------------------------*\
\r
6340 * About box dialog functions
\r
6342 \*---------------------------------------------------------------------------*/
\r
6344 /* Process messages for "About" dialog box */
\r
6346 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6348 switch (message) {
\r
6349 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6350 /* Center the dialog over the application window */
\r
6351 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6352 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6353 Translate(hDlg, ABOUTBOX);
\r
6357 case WM_COMMAND: /* message: received a command */
\r
6358 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6359 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6360 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6368 /*---------------------------------------------------------------------------*\
\r
6370 * Comment Dialog functions
\r
6372 \*---------------------------------------------------------------------------*/
\r
6375 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6377 static HANDLE hwndText = NULL;
\r
6378 int len, newSizeX, newSizeY, flags;
\r
6379 static int sizeX, sizeY;
\r
6384 switch (message) {
\r
6385 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6386 /* Initialize the dialog items */
\r
6387 Translate(hDlg, DLG_EditComment);
\r
6388 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6389 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6390 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6391 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6392 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6393 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6394 SetWindowText(hDlg, commentTitle);
\r
6395 if (editComment) {
\r
6396 SetFocus(hwndText);
\r
6398 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6400 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6401 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6402 MAKELPARAM(FALSE, 0));
\r
6403 /* Size and position the dialog */
\r
6404 if (!commentDialog) {
\r
6405 commentDialog = hDlg;
\r
6406 flags = SWP_NOZORDER;
\r
6407 GetClientRect(hDlg, &rect);
\r
6408 sizeX = rect.right;
\r
6409 sizeY = rect.bottom;
\r
6410 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6411 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6412 WINDOWPLACEMENT wp;
\r
6413 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6414 wp.length = sizeof(WINDOWPLACEMENT);
\r
6416 wp.showCmd = SW_SHOW;
\r
6417 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6418 wp.rcNormalPosition.left = wpComment.x;
\r
6419 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6420 wp.rcNormalPosition.top = wpComment.y;
\r
6421 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6422 SetWindowPlacement(hDlg, &wp);
\r
6424 GetClientRect(hDlg, &rect);
\r
6425 newSizeX = rect.right;
\r
6426 newSizeY = rect.bottom;
\r
6427 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6428 newSizeX, newSizeY);
\r
6433 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6436 case WM_COMMAND: /* message: received a command */
\r
6437 switch (LOWORD(wParam)) {
\r
6439 if (editComment) {
\r
6441 /* Read changed options from the dialog box */
\r
6442 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6443 len = GetWindowTextLength(hwndText);
\r
6444 str = (char *) malloc(len + 1);
\r
6445 GetWindowText(hwndText, str, len + 1);
\r
6454 ReplaceComment(commentIndex, str);
\r
6461 case OPT_CancelComment:
\r
6465 case OPT_ClearComment:
\r
6466 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6469 case OPT_EditComment:
\r
6470 EditCommentEvent();
\r
6478 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6479 if( wParam == OPT_CommentText ) {
\r
6480 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6482 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6483 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6487 pt.x = LOWORD( lpMF->lParam );
\r
6488 pt.y = HIWORD( lpMF->lParam );
\r
6490 if(lpMF->msg == WM_CHAR) {
\r
6492 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6493 index = sel.cpMin;
\r
6495 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6497 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6498 len = GetWindowTextLength(hwndText);
\r
6499 str = (char *) malloc(len + 1);
\r
6500 GetWindowText(hwndText, str, len + 1);
\r
6501 ReplaceComment(commentIndex, str);
\r
6502 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6503 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6506 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6507 lpMF->msg = WM_USER;
\r
6515 newSizeX = LOWORD(lParam);
\r
6516 newSizeY = HIWORD(lParam);
\r
6517 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6522 case WM_GETMINMAXINFO:
\r
6523 /* Prevent resizing window too small */
\r
6524 mmi = (MINMAXINFO *) lParam;
\r
6525 mmi->ptMinTrackSize.x = 100;
\r
6526 mmi->ptMinTrackSize.y = 100;
\r
6533 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6538 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6540 if (str == NULL) str = "";
\r
6541 p = (char *) malloc(2 * strlen(str) + 2);
\r
6544 if (*str == '\n') *q++ = '\r';
\r
6548 if (commentText != NULL) free(commentText);
\r
6550 commentIndex = index;
\r
6551 commentTitle = title;
\r
6553 editComment = edit;
\r
6555 if (commentDialog) {
\r
6556 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6557 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6559 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6560 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6561 hwndMain, (DLGPROC)lpProc);
\r
6562 FreeProcInstance(lpProc);
\r
6568 /*---------------------------------------------------------------------------*\
\r
6570 * Type-in move dialog functions
\r
6572 \*---------------------------------------------------------------------------*/
\r
6575 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6577 char move[MSG_SIZ];
\r
6580 switch (message) {
\r
6581 case WM_INITDIALOG:
\r
6582 move[0] = (char) lParam;
\r
6583 move[1] = NULLCHAR;
\r
6584 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6585 Translate(hDlg, DLG_TypeInMove);
\r
6586 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6587 SetWindowText(hInput, move);
\r
6589 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6593 switch (LOWORD(wParam)) {
\r
6596 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6597 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6598 TypeInDoneEvent(move);
\r
6599 EndDialog(hDlg, TRUE);
\r
6602 EndDialog(hDlg, FALSE);
\r
6613 PopUpMoveDialog(char firstchar)
\r
6617 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6618 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6619 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6620 FreeProcInstance(lpProc);
\r
6623 /*---------------------------------------------------------------------------*\
\r
6625 * Type-in name dialog functions
\r
6627 \*---------------------------------------------------------------------------*/
\r
6630 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6632 char move[MSG_SIZ];
\r
6635 switch (message) {
\r
6636 case WM_INITDIALOG:
\r
6637 move[0] = (char) lParam;
\r
6638 move[1] = NULLCHAR;
\r
6639 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6640 Translate(hDlg, DLG_TypeInName);
\r
6641 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6642 SetWindowText(hInput, move);
\r
6644 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6648 switch (LOWORD(wParam)) {
\r
6650 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6651 appData.userName = strdup(move);
\r
6654 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6655 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6656 DisplayTitle(move);
\r
6660 EndDialog(hDlg, TRUE);
\r
6663 EndDialog(hDlg, FALSE);
\r
6674 PopUpNameDialog(char firstchar)
\r
6678 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6679 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6680 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6681 FreeProcInstance(lpProc);
\r
6684 /*---------------------------------------------------------------------------*\
\r
6688 \*---------------------------------------------------------------------------*/
\r
6690 /* Nonmodal error box */
\r
6691 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6692 WPARAM wParam, LPARAM lParam);
\r
6695 ErrorPopUp(char *title, char *content)
\r
6699 BOOLEAN modal = hwndMain == NULL;
\r
6717 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6718 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6721 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6723 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6724 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6725 hwndMain, (DLGPROC)lpProc);
\r
6726 FreeProcInstance(lpProc);
\r
6733 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6734 if (errorDialog == NULL) return;
\r
6735 DestroyWindow(errorDialog);
\r
6736 errorDialog = NULL;
\r
6737 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6741 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6746 switch (message) {
\r
6747 case WM_INITDIALOG:
\r
6748 GetWindowRect(hDlg, &rChild);
\r
6751 SetWindowPos(hDlg, NULL, rChild.left,
\r
6752 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6753 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6757 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6758 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6759 and it doesn't work when you resize the dialog.
\r
6760 For now, just give it a default position.
\r
6762 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6763 Translate(hDlg, DLG_Error);
\r
6765 errorDialog = hDlg;
\r
6766 SetWindowText(hDlg, errorTitle);
\r
6767 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6768 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6772 switch (LOWORD(wParam)) {
\r
6775 if (errorDialog == hDlg) errorDialog = NULL;
\r
6776 DestroyWindow(hDlg);
\r
6788 HWND gothicDialog = NULL;
\r
6791 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6795 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6797 switch (message) {
\r
6798 case WM_INITDIALOG:
\r
6799 GetWindowRect(hDlg, &rChild);
\r
6801 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6805 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6806 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6807 and it doesn't work when you resize the dialog.
\r
6808 For now, just give it a default position.
\r
6810 gothicDialog = hDlg;
\r
6811 SetWindowText(hDlg, errorTitle);
\r
6812 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6813 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6817 switch (LOWORD(wParam)) {
\r
6820 if (errorDialog == hDlg) errorDialog = NULL;
\r
6821 DestroyWindow(hDlg);
\r
6833 GothicPopUp(char *title, VariantClass variant)
\r
6836 static char *lastTitle;
\r
6838 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6839 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6841 if(lastTitle != title && gothicDialog != NULL) {
\r
6842 DestroyWindow(gothicDialog);
\r
6843 gothicDialog = NULL;
\r
6845 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6846 title = lastTitle;
\r
6847 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6848 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6849 hwndMain, (DLGPROC)lpProc);
\r
6850 FreeProcInstance(lpProc);
\r
6855 /*---------------------------------------------------------------------------*\
\r
6857 * Ics Interaction console functions
\r
6859 \*---------------------------------------------------------------------------*/
\r
6861 #define HISTORY_SIZE 64
\r
6862 static char *history[HISTORY_SIZE];
\r
6863 int histIn = 0, histP = 0;
\r
6866 SaveInHistory(char *cmd)
\r
6868 if (history[histIn] != NULL) {
\r
6869 free(history[histIn]);
\r
6870 history[histIn] = NULL;
\r
6872 if (*cmd == NULLCHAR) return;
\r
6873 history[histIn] = StrSave(cmd);
\r
6874 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6875 if (history[histIn] != NULL) {
\r
6876 free(history[histIn]);
\r
6877 history[histIn] = NULL;
\r
6883 PrevInHistory(char *cmd)
\r
6886 if (histP == histIn) {
\r
6887 if (history[histIn] != NULL) free(history[histIn]);
\r
6888 history[histIn] = StrSave(cmd);
\r
6890 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6891 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6893 return history[histP];
\r
6899 if (histP == histIn) return NULL;
\r
6900 histP = (histP + 1) % HISTORY_SIZE;
\r
6901 return history[histP];
\r
6905 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6909 hmenu = LoadMenu(hInst, "TextMenu");
\r
6910 h = GetSubMenu(hmenu, 0);
\r
6912 if (strcmp(e->item, "-") == 0) {
\r
6913 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6914 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6915 int flags = MF_STRING, j = 0;
\r
6916 if (e->item[0] == '|') {
\r
6917 flags |= MF_MENUBARBREAK;
\r
6920 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6921 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6929 WNDPROC consoleTextWindowProc;
\r
6932 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6934 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6935 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6939 SetWindowText(hInput, command);
\r
6941 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6943 sel.cpMin = 999999;
\r
6944 sel.cpMax = 999999;
\r
6945 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6950 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6951 if (sel.cpMin == sel.cpMax) {
\r
6952 /* Expand to surrounding word */
\r
6955 tr.chrg.cpMax = sel.cpMin;
\r
6956 tr.chrg.cpMin = --sel.cpMin;
\r
6957 if (sel.cpMin < 0) break;
\r
6958 tr.lpstrText = name;
\r
6959 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6960 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6964 tr.chrg.cpMin = sel.cpMax;
\r
6965 tr.chrg.cpMax = ++sel.cpMax;
\r
6966 tr.lpstrText = name;
\r
6967 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6968 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6971 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6972 MessageBeep(MB_ICONEXCLAMATION);
\r
6976 tr.lpstrText = name;
\r
6977 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6979 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6980 MessageBeep(MB_ICONEXCLAMATION);
\r
6983 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6986 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6987 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6988 SetWindowText(hInput, buf);
\r
6989 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6991 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6992 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6993 SetWindowText(hInput, buf);
\r
6994 sel.cpMin = 999999;
\r
6995 sel.cpMax = 999999;
\r
6996 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7002 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7007 switch (message) {
\r
7009 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7010 if(wParam=='R') return 0;
\r
7013 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7016 sel.cpMin = 999999;
\r
7017 sel.cpMax = 999999;
\r
7018 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7019 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7024 if(wParam != '\022') {
\r
7025 if (wParam == '\t') {
\r
7026 if (GetKeyState(VK_SHIFT) < 0) {
\r
7028 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7029 if (buttonDesc[0].hwnd) {
\r
7030 SetFocus(buttonDesc[0].hwnd);
\r
7032 SetFocus(hwndMain);
\r
7036 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7039 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7040 JAWS_DELETE( SetFocus(hInput); )
\r
7041 SendMessage(hInput, message, wParam, lParam);
\r
7044 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7046 case WM_RBUTTONDOWN:
\r
7047 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7048 /* Move selection here if it was empty */
\r
7050 pt.x = LOWORD(lParam);
\r
7051 pt.y = HIWORD(lParam);
\r
7052 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7053 if (sel.cpMin == sel.cpMax) {
\r
7054 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7055 sel.cpMax = sel.cpMin;
\r
7056 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7058 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7059 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7061 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7062 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7063 if (sel.cpMin == sel.cpMax) {
\r
7064 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7065 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7067 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7068 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7070 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7071 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7072 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7073 MenuPopup(hwnd, pt, hmenu, -1);
\r
7077 case WM_RBUTTONUP:
\r
7078 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7079 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7080 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7084 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7086 return SendMessage(hInput, message, wParam, lParam);
\r
7087 case WM_MBUTTONDOWN:
\r
7088 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7090 switch (LOWORD(wParam)) {
\r
7091 case IDM_QuickPaste:
\r
7093 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7094 if (sel.cpMin == sel.cpMax) {
\r
7095 MessageBeep(MB_ICONEXCLAMATION);
\r
7098 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7099 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7100 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7105 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7108 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7111 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7115 int i = LOWORD(wParam) - IDM_CommandX;
\r
7116 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7117 icsTextMenuEntry[i].command != NULL) {
\r
7118 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7119 icsTextMenuEntry[i].getname,
\r
7120 icsTextMenuEntry[i].immediate);
\r
7128 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7131 WNDPROC consoleInputWindowProc;
\r
7134 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7136 char buf[MSG_SIZ];
\r
7138 static BOOL sendNextChar = FALSE;
\r
7139 static BOOL quoteNextChar = FALSE;
\r
7140 InputSource *is = consoleInputSource;
\r
7144 switch (message) {
\r
7146 if (!appData.localLineEditing || sendNextChar) {
\r
7147 is->buf[0] = (CHAR) wParam;
\r
7149 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7150 sendNextChar = FALSE;
\r
7153 if (quoteNextChar) {
\r
7154 buf[0] = (char) wParam;
\r
7155 buf[1] = NULLCHAR;
\r
7156 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7157 quoteNextChar = FALSE;
\r
7161 case '\r': /* Enter key */
\r
7162 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7163 if (consoleEcho) SaveInHistory(is->buf);
\r
7164 is->buf[is->count++] = '\n';
\r
7165 is->buf[is->count] = NULLCHAR;
\r
7166 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7167 if (consoleEcho) {
\r
7168 ConsoleOutput(is->buf, is->count, TRUE);
\r
7169 } else if (appData.localLineEditing) {
\r
7170 ConsoleOutput("\n", 1, TRUE);
\r
7173 case '\033': /* Escape key */
\r
7174 SetWindowText(hwnd, "");
\r
7175 cf.cbSize = sizeof(CHARFORMAT);
\r
7176 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7177 if (consoleEcho) {
\r
7178 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7180 cf.crTextColor = COLOR_ECHOOFF;
\r
7182 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7183 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7185 case '\t': /* Tab key */
\r
7186 if (GetKeyState(VK_SHIFT) < 0) {
\r
7188 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7191 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7192 if (buttonDesc[0].hwnd) {
\r
7193 SetFocus(buttonDesc[0].hwnd);
\r
7195 SetFocus(hwndMain);
\r
7199 case '\023': /* Ctrl+S */
\r
7200 sendNextChar = TRUE;
\r
7202 case '\021': /* Ctrl+Q */
\r
7203 quoteNextChar = TRUE;
\r
7213 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7214 p = PrevInHistory(buf);
\r
7216 SetWindowText(hwnd, p);
\r
7217 sel.cpMin = 999999;
\r
7218 sel.cpMax = 999999;
\r
7219 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7224 p = NextInHistory();
\r
7226 SetWindowText(hwnd, p);
\r
7227 sel.cpMin = 999999;
\r
7228 sel.cpMax = 999999;
\r
7229 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7235 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7239 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7243 case WM_MBUTTONDOWN:
\r
7244 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7245 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7247 case WM_RBUTTONUP:
\r
7248 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7249 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7250 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7254 hmenu = LoadMenu(hInst, "InputMenu");
\r
7255 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7256 if (sel.cpMin == sel.cpMax) {
\r
7257 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7258 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7260 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7261 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7263 pt.x = LOWORD(lParam);
\r
7264 pt.y = HIWORD(lParam);
\r
7265 MenuPopup(hwnd, pt, hmenu, -1);
\r
7269 switch (LOWORD(wParam)) {
\r
7271 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7273 case IDM_SelectAll:
\r
7275 sel.cpMax = -1; /*999999?*/
\r
7276 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7279 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7282 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7285 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7290 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7293 #define CO_MAX 100000
\r
7294 #define CO_TRIM 1000
\r
7297 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7299 static SnapData sd;
\r
7300 HWND hText, hInput;
\r
7302 static int sizeX, sizeY;
\r
7303 int newSizeX, newSizeY;
\r
7307 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7308 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7310 switch (message) {
\r
7312 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7314 ENLINK *pLink = (ENLINK*)lParam;
\r
7315 if (pLink->msg == WM_LBUTTONUP)
\r
7319 tr.chrg = pLink->chrg;
\r
7320 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7321 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7322 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7323 free(tr.lpstrText);
\r
7327 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7328 hwndConsole = hDlg;
\r
7330 consoleTextWindowProc = (WNDPROC)
\r
7331 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7332 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7333 consoleInputWindowProc = (WNDPROC)
\r
7334 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7335 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7336 Colorize(ColorNormal, TRUE);
\r
7337 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7338 ChangedConsoleFont();
\r
7339 GetClientRect(hDlg, &rect);
\r
7340 sizeX = rect.right;
\r
7341 sizeY = rect.bottom;
\r
7342 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7343 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7344 WINDOWPLACEMENT wp;
\r
7345 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7346 wp.length = sizeof(WINDOWPLACEMENT);
\r
7348 wp.showCmd = SW_SHOW;
\r
7349 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7350 wp.rcNormalPosition.left = wpConsole.x;
\r
7351 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7352 wp.rcNormalPosition.top = wpConsole.y;
\r
7353 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7354 SetWindowPlacement(hDlg, &wp);
\r
7357 // [HGM] Chessknight's change 2004-07-13
\r
7358 else { /* Determine Defaults */
\r
7359 WINDOWPLACEMENT wp;
\r
7360 wpConsole.x = wpMain.width + 1;
\r
7361 wpConsole.y = wpMain.y;
\r
7362 wpConsole.width = screenWidth - wpMain.width;
\r
7363 wpConsole.height = wpMain.height;
\r
7364 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7365 wp.length = sizeof(WINDOWPLACEMENT);
\r
7367 wp.showCmd = SW_SHOW;
\r
7368 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7369 wp.rcNormalPosition.left = wpConsole.x;
\r
7370 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7371 wp.rcNormalPosition.top = wpConsole.y;
\r
7372 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7373 SetWindowPlacement(hDlg, &wp);
\r
7376 // Allow hText to highlight URLs and send notifications on them
\r
7377 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7378 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7379 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7380 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7394 if (IsIconic(hDlg)) break;
\r
7395 newSizeX = LOWORD(lParam);
\r
7396 newSizeY = HIWORD(lParam);
\r
7397 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7398 RECT rectText, rectInput;
\r
7400 int newTextHeight, newTextWidth;
\r
7401 GetWindowRect(hText, &rectText);
\r
7402 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7403 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7404 if (newTextHeight < 0) {
\r
7405 newSizeY += -newTextHeight;
\r
7406 newTextHeight = 0;
\r
7408 SetWindowPos(hText, NULL, 0, 0,
\r
7409 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7410 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7411 pt.x = rectInput.left;
\r
7412 pt.y = rectInput.top + newSizeY - sizeY;
\r
7413 ScreenToClient(hDlg, &pt);
\r
7414 SetWindowPos(hInput, NULL,
\r
7415 pt.x, pt.y, /* needs client coords */
\r
7416 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7417 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7423 case WM_GETMINMAXINFO:
\r
7424 /* Prevent resizing window too small */
\r
7425 mmi = (MINMAXINFO *) lParam;
\r
7426 mmi->ptMinTrackSize.x = 100;
\r
7427 mmi->ptMinTrackSize.y = 100;
\r
7430 /* [AS] Snapping */
\r
7431 case WM_ENTERSIZEMOVE:
\r
7432 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7435 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7438 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7440 case WM_EXITSIZEMOVE:
\r
7441 UpdateICSWidth(hText);
\r
7442 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7445 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7453 if (hwndConsole) return;
\r
7454 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7455 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7460 ConsoleOutput(char* data, int length, int forceVisible)
\r
7465 char buf[CO_MAX+1];
\r
7468 static int delayLF = 0;
\r
7469 CHARRANGE savesel, sel;
\r
7471 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7479 while (length--) {
\r
7487 } else if (*p == '\007') {
\r
7488 MyPlaySound(&sounds[(int)SoundBell]);
\r
7495 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7496 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7497 /* Save current selection */
\r
7498 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7499 exlen = GetWindowTextLength(hText);
\r
7500 /* Find out whether current end of text is visible */
\r
7501 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7502 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7503 /* Trim existing text if it's too long */
\r
7504 if (exlen + (q - buf) > CO_MAX) {
\r
7505 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7508 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7509 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7511 savesel.cpMin -= trim;
\r
7512 savesel.cpMax -= trim;
\r
7513 if (exlen < 0) exlen = 0;
\r
7514 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7515 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7517 /* Append the new text */
\r
7518 sel.cpMin = exlen;
\r
7519 sel.cpMax = exlen;
\r
7520 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7521 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7522 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7523 if (forceVisible || exlen == 0 ||
\r
7524 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7525 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7526 /* Scroll to make new end of text visible if old end of text
\r
7527 was visible or new text is an echo of user typein */
\r
7528 sel.cpMin = 9999999;
\r
7529 sel.cpMax = 9999999;
\r
7530 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7531 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7532 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7533 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7535 if (savesel.cpMax == exlen || forceVisible) {
\r
7536 /* Move insert point to new end of text if it was at the old
\r
7537 end of text or if the new text is an echo of user typein */
\r
7538 sel.cpMin = 9999999;
\r
7539 sel.cpMax = 9999999;
\r
7540 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7542 /* Restore previous selection */
\r
7543 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7545 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7552 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7556 COLORREF oldFg, oldBg;
\r
7560 if(copyNumber > 1)
\r
7561 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7563 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7564 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7565 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7568 rect.right = x + squareSize;
\r
7570 rect.bottom = y + squareSize;
\r
7573 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7574 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7575 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7576 &rect, str, strlen(str), NULL);
\r
7578 (void) SetTextColor(hdc, oldFg);
\r
7579 (void) SetBkColor(hdc, oldBg);
\r
7580 (void) SelectObject(hdc, oldFont);
\r
7584 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7585 RECT *rect, char *color, char *flagFell)
\r
7589 COLORREF oldFg, oldBg;
\r
7592 if (twoBoards && partnerUp) return;
\r
7593 if (appData.clockMode) {
\r
7595 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7597 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7604 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7605 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7607 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7608 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7610 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7614 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7615 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7616 rect, str, strlen(str), NULL);
\r
7617 if(logoHeight > 0 && appData.clockMode) {
\r
7619 str += strlen(color)+2;
\r
7620 r.top = rect->top + logoHeight/2;
\r
7621 r.left = rect->left;
\r
7622 r.right = rect->right;
\r
7623 r.bottom = rect->bottom;
\r
7624 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7625 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7626 &r, str, strlen(str), NULL);
\r
7628 (void) SetTextColor(hdc, oldFg);
\r
7629 (void) SetBkColor(hdc, oldBg);
\r
7630 (void) SelectObject(hdc, oldFont);
\r
7635 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7641 if( count <= 0 ) {
\r
7642 if (appData.debugMode) {
\r
7643 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7646 return ERROR_INVALID_USER_BUFFER;
\r
7649 ResetEvent(ovl->hEvent);
\r
7650 ovl->Offset = ovl->OffsetHigh = 0;
\r
7651 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7655 err = GetLastError();
\r
7656 if (err == ERROR_IO_PENDING) {
\r
7657 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7661 err = GetLastError();
\r
7668 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7673 ResetEvent(ovl->hEvent);
\r
7674 ovl->Offset = ovl->OffsetHigh = 0;
\r
7675 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7679 err = GetLastError();
\r
7680 if (err == ERROR_IO_PENDING) {
\r
7681 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7685 err = GetLastError();
\r
7691 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7692 void CheckForInputBufferFull( InputSource * is )
\r
7694 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7695 /* Look for end of line */
\r
7696 char * p = is->buf;
\r
7698 while( p < is->next && *p != '\n' ) {
\r
7702 if( p >= is->next ) {
\r
7703 if (appData.debugMode) {
\r
7704 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7707 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7708 is->count = (DWORD) -1;
\r
7709 is->next = is->buf;
\r
7715 InputThread(LPVOID arg)
\r
7720 is = (InputSource *) arg;
\r
7721 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7722 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7723 while (is->hThread != NULL) {
\r
7724 is->error = DoReadFile(is->hFile, is->next,
\r
7725 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7726 &is->count, &ovl);
\r
7727 if (is->error == NO_ERROR) {
\r
7728 is->next += is->count;
\r
7730 if (is->error == ERROR_BROKEN_PIPE) {
\r
7731 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7734 is->count = (DWORD) -1;
\r
7735 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7740 CheckForInputBufferFull( is );
\r
7742 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7744 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7746 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7749 CloseHandle(ovl.hEvent);
\r
7750 CloseHandle(is->hFile);
\r
7752 if (appData.debugMode) {
\r
7753 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7760 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7762 NonOvlInputThread(LPVOID arg)
\r
7769 is = (InputSource *) arg;
\r
7770 while (is->hThread != NULL) {
\r
7771 is->error = ReadFile(is->hFile, is->next,
\r
7772 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7773 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7774 if (is->error == NO_ERROR) {
\r
7775 /* Change CRLF to LF */
\r
7776 if (is->next > is->buf) {
\r
7778 i = is->count + 1;
\r
7786 if (prev == '\r' && *p == '\n') {
\r
7798 if (is->error == ERROR_BROKEN_PIPE) {
\r
7799 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7802 is->count = (DWORD) -1;
\r
7806 CheckForInputBufferFull( is );
\r
7808 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7810 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7812 if (is->count < 0) break; /* Quit on error */
\r
7814 CloseHandle(is->hFile);
\r
7819 SocketInputThread(LPVOID arg)
\r
7823 is = (InputSource *) arg;
\r
7824 while (is->hThread != NULL) {
\r
7825 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7826 if ((int)is->count == SOCKET_ERROR) {
\r
7827 is->count = (DWORD) -1;
\r
7828 is->error = WSAGetLastError();
\r
7830 is->error = NO_ERROR;
\r
7831 is->next += is->count;
\r
7832 if (is->count == 0 && is->second == is) {
\r
7833 /* End of file on stderr; quit with no message */
\r
7837 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7839 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7841 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7847 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7851 is = (InputSource *) lParam;
\r
7852 if (is->lineByLine) {
\r
7853 /* Feed in lines one by one */
\r
7854 char *p = is->buf;
\r
7856 while (q < is->next) {
\r
7857 if (*q++ == '\n') {
\r
7858 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7863 /* Move any partial line to the start of the buffer */
\r
7865 while (p < is->next) {
\r
7870 if (is->error != NO_ERROR || is->count == 0) {
\r
7871 /* Notify backend of the error. Note: If there was a partial
\r
7872 line at the end, it is not flushed through. */
\r
7873 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7876 /* Feed in the whole chunk of input at once */
\r
7877 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7878 is->next = is->buf;
\r
7882 /*---------------------------------------------------------------------------*\
\r
7884 * Menu enables. Used when setting various modes.
\r
7886 \*---------------------------------------------------------------------------*/
\r
7894 GreyRevert(Boolean grey)
\r
7895 { // [HGM] vari: for retracting variations in local mode
\r
7896 HMENU hmenu = GetMenu(hwndMain);
\r
7897 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7898 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7902 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7904 while (enab->item > 0) {
\r
7905 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7910 Enables gnuEnables[] = {
\r
7911 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7925 // Needed to switch from ncp to GNU mode on Engine Load
\r
7926 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7927 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7936 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7937 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7943 Enables icsEnables[] = {
\r
7944 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7952 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7960 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7968 Enables zippyEnables[] = {
\r
7969 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7970 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7971 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7972 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7977 Enables ncpEnables[] = {
\r
7978 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7987 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7988 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7989 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7990 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7991 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7992 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7993 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7994 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8003 Enables trainingOnEnables[] = {
\r
8004 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8016 Enables trainingOffEnables[] = {
\r
8017 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8018 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8019 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8020 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8021 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8025 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8029 /* These modify either ncpEnables or gnuEnables */
\r
8030 Enables cmailEnables[] = {
\r
8031 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8032 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8033 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8034 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8041 Enables machineThinkingEnables[] = {
\r
8042 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8044 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8050 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8051 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8052 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8054 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8055 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8056 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8057 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8061 Enables userThinkingEnables[] = {
\r
8062 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8064 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8065 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8066 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8067 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8069 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8070 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8071 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8072 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8073 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8074 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8075 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8076 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8077 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8081 /*---------------------------------------------------------------------------*\
\r
8083 * Front-end interface functions exported by XBoard.
\r
8084 * Functions appear in same order as prototypes in frontend.h.
\r
8086 \*---------------------------------------------------------------------------*/
\r
8088 CheckMark(UINT item, int state)
\r
8090 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8096 static UINT prevChecked = 0;
\r
8097 static int prevPausing = 0;
\r
8100 if (pausing != prevPausing) {
\r
8101 prevPausing = pausing;
\r
8102 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8103 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8104 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8107 switch (gameMode) {
\r
8108 case BeginningOfGame:
\r
8109 if (appData.icsActive)
\r
8110 nowChecked = IDM_IcsClient;
\r
8111 else if (appData.noChessProgram)
\r
8112 nowChecked = IDM_EditGame;
\r
8114 nowChecked = IDM_MachineBlack;
\r
8116 case MachinePlaysBlack:
\r
8117 nowChecked = IDM_MachineBlack;
\r
8119 case MachinePlaysWhite:
\r
8120 nowChecked = IDM_MachineWhite;
\r
8122 case TwoMachinesPlay:
\r
8123 nowChecked = IDM_TwoMachines;
\r
8126 nowChecked = IDM_AnalysisMode;
\r
8129 nowChecked = IDM_AnalyzeFile;
\r
8132 nowChecked = IDM_EditGame;
\r
8134 case PlayFromGameFile:
\r
8135 nowChecked = IDM_LoadGame;
\r
8137 case EditPosition:
\r
8138 nowChecked = IDM_EditPosition;
\r
8141 nowChecked = IDM_Training;
\r
8143 case IcsPlayingWhite:
\r
8144 case IcsPlayingBlack:
\r
8145 case IcsObserving:
\r
8147 nowChecked = IDM_IcsClient;
\r
8154 CheckMark(prevChecked, MF_UNCHECKED);
\r
8155 CheckMark(nowChecked, MF_CHECKED);
\r
8156 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8158 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8159 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8160 MF_BYCOMMAND|MF_ENABLED);
\r
8162 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8163 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8166 prevChecked = nowChecked;
\r
8168 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8169 if (appData.icsActive) {
\r
8170 if (appData.icsEngineAnalyze) {
\r
8171 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8173 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8176 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8182 HMENU hmenu = GetMenu(hwndMain);
\r
8183 SetMenuEnables(hmenu, icsEnables);
\r
8184 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8185 MF_BYCOMMAND|MF_ENABLED);
\r
8187 if (appData.zippyPlay) {
\r
8188 SetMenuEnables(hmenu, zippyEnables);
\r
8189 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8190 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8191 MF_BYCOMMAND|MF_ENABLED);
\r
8199 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8205 HMENU hmenu = GetMenu(hwndMain);
\r
8206 SetMenuEnables(hmenu, ncpEnables);
\r
8207 DrawMenuBar(hwndMain);
\r
8213 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8217 SetTrainingModeOn()
\r
8220 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8221 for (i = 0; i < N_BUTTONS; i++) {
\r
8222 if (buttonDesc[i].hwnd != NULL)
\r
8223 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8228 VOID SetTrainingModeOff()
\r
8231 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8232 for (i = 0; i < N_BUTTONS; i++) {
\r
8233 if (buttonDesc[i].hwnd != NULL)
\r
8234 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8240 SetUserThinkingEnables()
\r
8242 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8246 SetMachineThinkingEnables()
\r
8248 HMENU hMenu = GetMenu(hwndMain);
\r
8249 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8251 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8253 if (gameMode == MachinePlaysBlack) {
\r
8254 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8255 } else if (gameMode == MachinePlaysWhite) {
\r
8256 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8257 } else if (gameMode == TwoMachinesPlay) {
\r
8258 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8264 DisplayTitle(char *str)
\r
8266 char title[MSG_SIZ], *host;
\r
8267 if (str[0] != NULLCHAR) {
\r
8268 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8269 } else if (appData.icsActive) {
\r
8270 if (appData.icsCommPort[0] != NULLCHAR)
\r
8273 host = appData.icsHost;
\r
8274 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8275 } else if (appData.noChessProgram) {
\r
8276 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8278 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8279 strcat(title, ": ");
\r
8280 strcat(title, first.tidy);
\r
8282 SetWindowText(hwndMain, title);
\r
8287 DisplayMessage(char *str1, char *str2)
\r
8291 int remain = MESSAGE_TEXT_MAX - 1;
\r
8294 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8295 messageText[0] = NULLCHAR;
\r
8297 len = strlen(str1);
\r
8298 if (len > remain) len = remain;
\r
8299 strncpy(messageText, str1, len);
\r
8300 messageText[len] = NULLCHAR;
\r
8303 if (*str2 && remain >= 2) {
\r
8305 strcat(messageText, " ");
\r
8308 len = strlen(str2);
\r
8309 if (len > remain) len = remain;
\r
8310 strncat(messageText, str2, len);
\r
8312 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8313 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8315 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8319 hdc = GetDC(hwndMain);
\r
8320 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8321 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8322 &messageRect, messageText, strlen(messageText), NULL);
\r
8323 (void) SelectObject(hdc, oldFont);
\r
8324 (void) ReleaseDC(hwndMain, hdc);
\r
8328 DisplayError(char *str, int error)
\r
8330 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8334 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8336 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8337 NULL, error, LANG_NEUTRAL,
\r
8338 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8340 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8342 ErrorMap *em = errmap;
\r
8343 while (em->err != 0 && em->err != error) em++;
\r
8344 if (em->err != 0) {
\r
8345 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8347 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8352 ErrorPopUp(_("Error"), buf);
\r
8357 DisplayMoveError(char *str)
\r
8359 fromX = fromY = -1;
\r
8360 ClearHighlights();
\r
8361 DrawPosition(FALSE, NULL);
\r
8362 if (appData.popupMoveErrors) {
\r
8363 ErrorPopUp(_("Error"), str);
\r
8365 DisplayMessage(str, "");
\r
8366 moveErrorMessageUp = TRUE;
\r
8371 DisplayFatalError(char *str, int error, int exitStatus)
\r
8373 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8375 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8378 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8379 NULL, error, LANG_NEUTRAL,
\r
8380 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8382 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8384 ErrorMap *em = errmap;
\r
8385 while (em->err != 0 && em->err != error) em++;
\r
8386 if (em->err != 0) {
\r
8387 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8389 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8394 if (appData.debugMode) {
\r
8395 fprintf(debugFP, "%s: %s\n", label, str);
\r
8397 if (appData.popupExitMessage) {
\r
8398 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8399 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8401 ExitEvent(exitStatus);
\r
8406 DisplayInformation(char *str)
\r
8408 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8413 DisplayNote(char *str)
\r
8415 ErrorPopUp(_("Note"), str);
\r
8420 char *title, *question, *replyPrefix;
\r
8425 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8427 static QuestionParams *qp;
\r
8428 char reply[MSG_SIZ];
\r
8431 switch (message) {
\r
8432 case WM_INITDIALOG:
\r
8433 qp = (QuestionParams *) lParam;
\r
8434 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8435 Translate(hDlg, DLG_Question);
\r
8436 SetWindowText(hDlg, qp->title);
\r
8437 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8438 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8442 switch (LOWORD(wParam)) {
\r
8444 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8445 if (*reply) strcat(reply, " ");
\r
8446 len = strlen(reply);
\r
8447 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8448 strcat(reply, "\n");
\r
8449 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8450 EndDialog(hDlg, TRUE);
\r
8451 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8454 EndDialog(hDlg, FALSE);
\r
8465 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8467 QuestionParams qp;
\r
8471 qp.question = question;
\r
8472 qp.replyPrefix = replyPrefix;
\r
8474 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8475 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8476 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8477 FreeProcInstance(lpProc);
\r
8480 /* [AS] Pick FRC position */
\r
8481 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8483 static int * lpIndexFRC;
\r
8489 case WM_INITDIALOG:
\r
8490 lpIndexFRC = (int *) lParam;
\r
8492 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8493 Translate(hDlg, DLG_NewGameFRC);
\r
8495 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8496 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8497 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8498 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8503 switch( LOWORD(wParam) ) {
\r
8505 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8506 EndDialog( hDlg, 0 );
\r
8507 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8510 EndDialog( hDlg, 1 );
\r
8512 case IDC_NFG_Edit:
\r
8513 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8514 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8516 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8519 case IDC_NFG_Random:
\r
8520 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8521 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8534 int index = appData.defaultFrcPosition;
\r
8535 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8537 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8539 if( result == 0 ) {
\r
8540 appData.defaultFrcPosition = index;
\r
8546 /* [AS] Game list options. Refactored by HGM */
\r
8548 HWND gameListOptionsDialog;
\r
8550 // low-level front-end: clear text edit / list widget
\r
8554 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8557 // low-level front-end: clear text edit / list widget
\r
8559 GLT_DeSelectList()
\r
8561 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8564 // low-level front-end: append line to text edit / list widget
\r
8566 GLT_AddToList( char *name )
\r
8569 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8573 // low-level front-end: get line from text edit / list widget
\r
8575 GLT_GetFromList( int index, char *name )
\r
8578 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8584 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8586 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8587 int idx2 = idx1 + delta;
\r
8588 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8590 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8593 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8594 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8595 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8596 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8600 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8604 case WM_INITDIALOG:
\r
8605 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8607 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8608 Translate(hDlg, DLG_GameListOptions);
\r
8610 /* Initialize list */
\r
8611 GLT_TagsToList( lpUserGLT );
\r
8613 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8618 switch( LOWORD(wParam) ) {
\r
8621 EndDialog( hDlg, 0 );
\r
8624 EndDialog( hDlg, 1 );
\r
8627 case IDC_GLT_Default:
\r
8628 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8631 case IDC_GLT_Restore:
\r
8632 GLT_TagsToList( appData.gameListTags );
\r
8636 GLT_MoveSelection( hDlg, -1 );
\r
8639 case IDC_GLT_Down:
\r
8640 GLT_MoveSelection( hDlg, +1 );
\r
8650 int GameListOptions()
\r
8653 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8655 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8657 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8659 if( result == 0 ) {
\r
8660 /* [AS] Memory leak here! */
\r
8661 appData.gameListTags = strdup( lpUserGLT );
\r
8668 DisplayIcsInteractionTitle(char *str)
\r
8670 char consoleTitle[MSG_SIZ];
\r
8672 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8673 SetWindowText(hwndConsole, consoleTitle);
\r
8675 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8676 char buf[MSG_SIZ], *p = buf, *q;
\r
8677 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8679 q = strchr(p, ';');
\r
8681 if(*p) ChatPopUp(p);
\r
8685 SetActiveWindow(hwndMain);
\r
8689 DrawPosition(int fullRedraw, Board board)
\r
8691 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8694 void NotifyFrontendLogin()
\r
8697 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8703 fromX = fromY = -1;
\r
8704 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8705 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8706 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8707 dragInfo.lastpos = dragInfo.pos;
\r
8708 dragInfo.start.x = dragInfo.start.y = -1;
\r
8709 dragInfo.from = dragInfo.start;
\r
8711 DrawPosition(TRUE, NULL);
\r
8718 CommentPopUp(char *title, char *str)
\r
8720 HWND hwnd = GetActiveWindow();
\r
8721 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8723 SetActiveWindow(hwnd);
\r
8727 CommentPopDown(void)
\r
8729 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8730 if (commentDialog) {
\r
8731 ShowWindow(commentDialog, SW_HIDE);
\r
8733 commentUp = FALSE;
\r
8737 EditCommentPopUp(int index, char *title, char *str)
\r
8739 EitherCommentPopUp(index, title, str, TRUE);
\r
8746 MyPlaySound(&sounds[(int)SoundMove]);
\r
8749 VOID PlayIcsWinSound()
\r
8751 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8754 VOID PlayIcsLossSound()
\r
8756 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8759 VOID PlayIcsDrawSound()
\r
8761 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8764 VOID PlayIcsUnfinishedSound()
\r
8766 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8772 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8778 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8786 consoleEcho = TRUE;
\r
8787 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8788 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8789 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8798 consoleEcho = FALSE;
\r
8799 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8800 /* This works OK: set text and background both to the same color */
\r
8802 cf.crTextColor = COLOR_ECHOOFF;
\r
8803 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8804 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8807 /* No Raw()...? */
\r
8809 void Colorize(ColorClass cc, int continuation)
\r
8811 currentColorClass = cc;
\r
8812 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8813 consoleCF.crTextColor = textAttribs[cc].color;
\r
8814 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8815 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8821 static char buf[MSG_SIZ];
\r
8822 DWORD bufsiz = MSG_SIZ;
\r
8824 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8825 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8827 if (!GetUserName(buf, &bufsiz)) {
\r
8828 /*DisplayError("Error getting user name", GetLastError());*/
\r
8829 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8837 static char buf[MSG_SIZ];
\r
8838 DWORD bufsiz = MSG_SIZ;
\r
8840 if (!GetComputerName(buf, &bufsiz)) {
\r
8841 /*DisplayError("Error getting host name", GetLastError());*/
\r
8842 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8849 ClockTimerRunning()
\r
8851 return clockTimerEvent != 0;
\r
8857 if (clockTimerEvent == 0) return FALSE;
\r
8858 KillTimer(hwndMain, clockTimerEvent);
\r
8859 clockTimerEvent = 0;
\r
8864 StartClockTimer(long millisec)
\r
8866 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8867 (UINT) millisec, NULL);
\r
8871 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8874 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8876 if(appData.noGUI) return;
\r
8877 hdc = GetDC(hwndMain);
\r
8878 if (!IsIconic(hwndMain)) {
\r
8879 DisplayAClock(hdc, timeRemaining, highlight,
\r
8880 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8882 if (highlight && iconCurrent == iconBlack) {
\r
8883 iconCurrent = iconWhite;
\r
8884 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8885 if (IsIconic(hwndMain)) {
\r
8886 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8889 (void) ReleaseDC(hwndMain, hdc);
\r
8891 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8895 DisplayBlackClock(long timeRemaining, int highlight)
\r
8898 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8900 if(appData.noGUI) return;
\r
8901 hdc = GetDC(hwndMain);
\r
8902 if (!IsIconic(hwndMain)) {
\r
8903 DisplayAClock(hdc, timeRemaining, highlight,
\r
8904 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8906 if (highlight && iconCurrent == iconWhite) {
\r
8907 iconCurrent = iconBlack;
\r
8908 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8909 if (IsIconic(hwndMain)) {
\r
8910 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8913 (void) ReleaseDC(hwndMain, hdc);
\r
8915 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8920 LoadGameTimerRunning()
\r
8922 return loadGameTimerEvent != 0;
\r
8926 StopLoadGameTimer()
\r
8928 if (loadGameTimerEvent == 0) return FALSE;
\r
8929 KillTimer(hwndMain, loadGameTimerEvent);
\r
8930 loadGameTimerEvent = 0;
\r
8935 StartLoadGameTimer(long millisec)
\r
8937 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8938 (UINT) millisec, NULL);
\r
8946 char fileTitle[MSG_SIZ];
\r
8948 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8949 f = OpenFileDialog(hwndMain, "a", defName,
\r
8950 appData.oldSaveStyle ? "gam" : "pgn",
\r
8952 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8954 SaveGame(f, 0, "");
\r
8961 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8963 if (delayedTimerEvent != 0) {
\r
8964 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8965 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8967 KillTimer(hwndMain, delayedTimerEvent);
\r
8968 delayedTimerEvent = 0;
\r
8969 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8970 delayedTimerCallback();
\r
8972 delayedTimerCallback = cb;
\r
8973 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8974 (UINT) millisec, NULL);
\r
8977 DelayedEventCallback
\r
8980 if (delayedTimerEvent) {
\r
8981 return delayedTimerCallback;
\r
8988 CancelDelayedEvent()
\r
8990 if (delayedTimerEvent) {
\r
8991 KillTimer(hwndMain, delayedTimerEvent);
\r
8992 delayedTimerEvent = 0;
\r
8996 DWORD GetWin32Priority(int nice)
\r
8997 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8999 REALTIME_PRIORITY_CLASS 0x00000100
\r
9000 HIGH_PRIORITY_CLASS 0x00000080
\r
9001 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9002 NORMAL_PRIORITY_CLASS 0x00000020
\r
9003 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9004 IDLE_PRIORITY_CLASS 0x00000040
\r
9006 if (nice < -15) return 0x00000080;
\r
9007 if (nice < 0) return 0x00008000;
\r
9008 if (nice == 0) return 0x00000020;
\r
9009 if (nice < 15) return 0x00004000;
\r
9010 return 0x00000040;
\r
9013 void RunCommand(char *cmdLine)
\r
9015 /* Now create the child process. */
\r
9016 STARTUPINFO siStartInfo;
\r
9017 PROCESS_INFORMATION piProcInfo;
\r
9019 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9020 siStartInfo.lpReserved = NULL;
\r
9021 siStartInfo.lpDesktop = NULL;
\r
9022 siStartInfo.lpTitle = NULL;
\r
9023 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9024 siStartInfo.cbReserved2 = 0;
\r
9025 siStartInfo.lpReserved2 = NULL;
\r
9026 siStartInfo.hStdInput = NULL;
\r
9027 siStartInfo.hStdOutput = NULL;
\r
9028 siStartInfo.hStdError = NULL;
\r
9030 CreateProcess(NULL,
\r
9031 cmdLine, /* command line */
\r
9032 NULL, /* process security attributes */
\r
9033 NULL, /* primary thread security attrs */
\r
9034 TRUE, /* handles are inherited */
\r
9035 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9036 NULL, /* use parent's environment */
\r
9038 &siStartInfo, /* STARTUPINFO pointer */
\r
9039 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9041 CloseHandle(piProcInfo.hThread);
\r
9044 /* Start a child process running the given program.
\r
9045 The process's standard output can be read from "from", and its
\r
9046 standard input can be written to "to".
\r
9047 Exit with fatal error if anything goes wrong.
\r
9048 Returns an opaque pointer that can be used to destroy the process
\r
9052 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9054 #define BUFSIZE 4096
\r
9056 HANDLE hChildStdinRd, hChildStdinWr,
\r
9057 hChildStdoutRd, hChildStdoutWr;
\r
9058 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9059 SECURITY_ATTRIBUTES saAttr;
\r
9061 PROCESS_INFORMATION piProcInfo;
\r
9062 STARTUPINFO siStartInfo;
\r
9064 char buf[MSG_SIZ];
\r
9067 if (appData.debugMode) {
\r
9068 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9073 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9074 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9075 saAttr.bInheritHandle = TRUE;
\r
9076 saAttr.lpSecurityDescriptor = NULL;
\r
9079 * The steps for redirecting child's STDOUT:
\r
9080 * 1. Create anonymous pipe to be STDOUT for child.
\r
9081 * 2. Create a noninheritable duplicate of read handle,
\r
9082 * and close the inheritable read handle.
\r
9085 /* Create a pipe for the child's STDOUT. */
\r
9086 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9087 return GetLastError();
\r
9090 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9091 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9092 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9093 FALSE, /* not inherited */
\r
9094 DUPLICATE_SAME_ACCESS);
\r
9096 return GetLastError();
\r
9098 CloseHandle(hChildStdoutRd);
\r
9101 * The steps for redirecting child's STDIN:
\r
9102 * 1. Create anonymous pipe to be STDIN for child.
\r
9103 * 2. Create a noninheritable duplicate of write handle,
\r
9104 * and close the inheritable write handle.
\r
9107 /* Create a pipe for the child's STDIN. */
\r
9108 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9109 return GetLastError();
\r
9112 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9113 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9114 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9115 FALSE, /* not inherited */
\r
9116 DUPLICATE_SAME_ACCESS);
\r
9118 return GetLastError();
\r
9120 CloseHandle(hChildStdinWr);
\r
9122 /* Arrange to (1) look in dir for the child .exe file, and
\r
9123 * (2) have dir be the child's working directory. Interpret
\r
9124 * dir relative to the directory WinBoard loaded from. */
\r
9125 GetCurrentDirectory(MSG_SIZ, buf);
\r
9126 SetCurrentDirectory(installDir);
\r
9127 SetCurrentDirectory(dir);
\r
9129 /* Now create the child process. */
\r
9131 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9132 siStartInfo.lpReserved = NULL;
\r
9133 siStartInfo.lpDesktop = NULL;
\r
9134 siStartInfo.lpTitle = NULL;
\r
9135 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9136 siStartInfo.cbReserved2 = 0;
\r
9137 siStartInfo.lpReserved2 = NULL;
\r
9138 siStartInfo.hStdInput = hChildStdinRd;
\r
9139 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9140 siStartInfo.hStdError = hChildStdoutWr;
\r
9142 fSuccess = CreateProcess(NULL,
\r
9143 cmdLine, /* command line */
\r
9144 NULL, /* process security attributes */
\r
9145 NULL, /* primary thread security attrs */
\r
9146 TRUE, /* handles are inherited */
\r
9147 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9148 NULL, /* use parent's environment */
\r
9150 &siStartInfo, /* STARTUPINFO pointer */
\r
9151 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9153 err = GetLastError();
\r
9154 SetCurrentDirectory(buf); /* return to prev directory */
\r
9159 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9160 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9161 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9164 /* Close the handles we don't need in the parent */
\r
9165 CloseHandle(piProcInfo.hThread);
\r
9166 CloseHandle(hChildStdinRd);
\r
9167 CloseHandle(hChildStdoutWr);
\r
9169 /* Prepare return value */
\r
9170 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9171 cp->kind = CPReal;
\r
9172 cp->hProcess = piProcInfo.hProcess;
\r
9173 cp->pid = piProcInfo.dwProcessId;
\r
9174 cp->hFrom = hChildStdoutRdDup;
\r
9175 cp->hTo = hChildStdinWrDup;
\r
9177 *pr = (void *) cp;
\r
9179 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9180 2000 where engines sometimes don't see the initial command(s)
\r
9181 from WinBoard and hang. I don't understand how that can happen,
\r
9182 but the Sleep is harmless, so I've put it in. Others have also
\r
9183 reported what may be the same problem, so hopefully this will fix
\r
9184 it for them too. */
\r
9192 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9194 ChildProc *cp; int result;
\r
9196 cp = (ChildProc *) pr;
\r
9197 if (cp == NULL) return;
\r
9199 switch (cp->kind) {
\r
9201 /* TerminateProcess is considered harmful, so... */
\r
9202 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9203 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9204 /* The following doesn't work because the chess program
\r
9205 doesn't "have the same console" as WinBoard. Maybe
\r
9206 we could arrange for this even though neither WinBoard
\r
9207 nor the chess program uses a console for stdio? */
\r
9208 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9210 /* [AS] Special termination modes for misbehaving programs... */
\r
9211 if( signal == 9 ) {
\r
9212 result = TerminateProcess( cp->hProcess, 0 );
\r
9214 if ( appData.debugMode) {
\r
9215 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9218 else if( signal == 10 ) {
\r
9219 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9221 if( dw != WAIT_OBJECT_0 ) {
\r
9222 result = TerminateProcess( cp->hProcess, 0 );
\r
9224 if ( appData.debugMode) {
\r
9225 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9231 CloseHandle(cp->hProcess);
\r
9235 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9239 closesocket(cp->sock);
\r
9244 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9245 closesocket(cp->sock);
\r
9246 closesocket(cp->sock2);
\r
9254 InterruptChildProcess(ProcRef pr)
\r
9258 cp = (ChildProc *) pr;
\r
9259 if (cp == NULL) return;
\r
9260 switch (cp->kind) {
\r
9262 /* The following doesn't work because the chess program
\r
9263 doesn't "have the same console" as WinBoard. Maybe
\r
9264 we could arrange for this even though neither WinBoard
\r
9265 nor the chess program uses a console for stdio */
\r
9266 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9271 /* Can't interrupt */
\r
9275 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9282 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9284 char cmdLine[MSG_SIZ];
\r
9286 if (port[0] == NULLCHAR) {
\r
9287 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9289 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9291 return StartChildProcess(cmdLine, "", pr);
\r
9295 /* Code to open TCP sockets */
\r
9298 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9304 struct sockaddr_in sa, mysa;
\r
9305 struct hostent FAR *hp;
\r
9306 unsigned short uport;
\r
9307 WORD wVersionRequested;
\r
9310 /* Initialize socket DLL */
\r
9311 wVersionRequested = MAKEWORD(1, 1);
\r
9312 err = WSAStartup(wVersionRequested, &wsaData);
\r
9313 if (err != 0) return err;
\r
9316 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9317 err = WSAGetLastError();
\r
9322 /* Bind local address using (mostly) don't-care values.
\r
9324 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9325 mysa.sin_family = AF_INET;
\r
9326 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9327 uport = (unsigned short) 0;
\r
9328 mysa.sin_port = htons(uport);
\r
9329 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9330 == SOCKET_ERROR) {
\r
9331 err = WSAGetLastError();
\r
9336 /* Resolve remote host name */
\r
9337 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9338 if (!(hp = gethostbyname(host))) {
\r
9339 unsigned int b0, b1, b2, b3;
\r
9341 err = WSAGetLastError();
\r
9343 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9344 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9345 hp->h_addrtype = AF_INET;
\r
9347 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9348 hp->h_addr_list[0] = (char *) malloc(4);
\r
9349 hp->h_addr_list[0][0] = (char) b0;
\r
9350 hp->h_addr_list[0][1] = (char) b1;
\r
9351 hp->h_addr_list[0][2] = (char) b2;
\r
9352 hp->h_addr_list[0][3] = (char) b3;
\r
9358 sa.sin_family = hp->h_addrtype;
\r
9359 uport = (unsigned short) atoi(port);
\r
9360 sa.sin_port = htons(uport);
\r
9361 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9363 /* Make connection */
\r
9364 if (connect(s, (struct sockaddr *) &sa,
\r
9365 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9366 err = WSAGetLastError();
\r
9371 /* Prepare return value */
\r
9372 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9373 cp->kind = CPSock;
\r
9375 *pr = (ProcRef *) cp;
\r
9381 OpenCommPort(char *name, ProcRef *pr)
\r
9386 char fullname[MSG_SIZ];
\r
9388 if (*name != '\\')
\r
9389 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9391 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9393 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9394 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9395 if (h == (HANDLE) -1) {
\r
9396 return GetLastError();
\r
9400 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9402 /* Accumulate characters until a 100ms pause, then parse */
\r
9403 ct.ReadIntervalTimeout = 100;
\r
9404 ct.ReadTotalTimeoutMultiplier = 0;
\r
9405 ct.ReadTotalTimeoutConstant = 0;
\r
9406 ct.WriteTotalTimeoutMultiplier = 0;
\r
9407 ct.WriteTotalTimeoutConstant = 0;
\r
9408 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9410 /* Prepare return value */
\r
9411 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9412 cp->kind = CPComm;
\r
9415 *pr = (ProcRef *) cp;
\r
9421 OpenLoopback(ProcRef *pr)
\r
9423 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9429 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9434 struct sockaddr_in sa, mysa;
\r
9435 struct hostent FAR *hp;
\r
9436 unsigned short uport;
\r
9437 WORD wVersionRequested;
\r
9440 char stderrPortStr[MSG_SIZ];
\r
9442 /* Initialize socket DLL */
\r
9443 wVersionRequested = MAKEWORD(1, 1);
\r
9444 err = WSAStartup(wVersionRequested, &wsaData);
\r
9445 if (err != 0) return err;
\r
9447 /* Resolve remote host name */
\r
9448 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9449 if (!(hp = gethostbyname(host))) {
\r
9450 unsigned int b0, b1, b2, b3;
\r
9452 err = WSAGetLastError();
\r
9454 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9455 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9456 hp->h_addrtype = AF_INET;
\r
9458 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9459 hp->h_addr_list[0] = (char *) malloc(4);
\r
9460 hp->h_addr_list[0][0] = (char) b0;
\r
9461 hp->h_addr_list[0][1] = (char) b1;
\r
9462 hp->h_addr_list[0][2] = (char) b2;
\r
9463 hp->h_addr_list[0][3] = (char) b3;
\r
9469 sa.sin_family = hp->h_addrtype;
\r
9470 uport = (unsigned short) 514;
\r
9471 sa.sin_port = htons(uport);
\r
9472 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9474 /* Bind local socket to unused "privileged" port address
\r
9476 s = INVALID_SOCKET;
\r
9477 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9478 mysa.sin_family = AF_INET;
\r
9479 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9480 for (fromPort = 1023;; fromPort--) {
\r
9481 if (fromPort < 0) {
\r
9483 return WSAEADDRINUSE;
\r
9485 if (s == INVALID_SOCKET) {
\r
9486 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9487 err = WSAGetLastError();
\r
9492 uport = (unsigned short) fromPort;
\r
9493 mysa.sin_port = htons(uport);
\r
9494 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9495 == SOCKET_ERROR) {
\r
9496 err = WSAGetLastError();
\r
9497 if (err == WSAEADDRINUSE) continue;
\r
9501 if (connect(s, (struct sockaddr *) &sa,
\r
9502 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9503 err = WSAGetLastError();
\r
9504 if (err == WSAEADDRINUSE) {
\r
9515 /* Bind stderr local socket to unused "privileged" port address
\r
9517 s2 = INVALID_SOCKET;
\r
9518 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9519 mysa.sin_family = AF_INET;
\r
9520 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9521 for (fromPort = 1023;; fromPort--) {
\r
9522 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9523 if (fromPort < 0) {
\r
9524 (void) closesocket(s);
\r
9526 return WSAEADDRINUSE;
\r
9528 if (s2 == INVALID_SOCKET) {
\r
9529 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9530 err = WSAGetLastError();
\r
9536 uport = (unsigned short) fromPort;
\r
9537 mysa.sin_port = htons(uport);
\r
9538 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9539 == SOCKET_ERROR) {
\r
9540 err = WSAGetLastError();
\r
9541 if (err == WSAEADDRINUSE) continue;
\r
9542 (void) closesocket(s);
\r
9546 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9547 err = WSAGetLastError();
\r
9548 if (err == WSAEADDRINUSE) {
\r
9550 s2 = INVALID_SOCKET;
\r
9553 (void) closesocket(s);
\r
9554 (void) closesocket(s2);
\r
9560 prevStderrPort = fromPort; // remember port used
\r
9561 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9563 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9564 err = WSAGetLastError();
\r
9565 (void) closesocket(s);
\r
9566 (void) closesocket(s2);
\r
9571 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9572 err = WSAGetLastError();
\r
9573 (void) closesocket(s);
\r
9574 (void) closesocket(s2);
\r
9578 if (*user == NULLCHAR) user = UserName();
\r
9579 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9580 err = WSAGetLastError();
\r
9581 (void) closesocket(s);
\r
9582 (void) closesocket(s2);
\r
9586 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9587 err = WSAGetLastError();
\r
9588 (void) closesocket(s);
\r
9589 (void) closesocket(s2);
\r
9594 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9595 err = WSAGetLastError();
\r
9596 (void) closesocket(s);
\r
9597 (void) closesocket(s2);
\r
9601 (void) closesocket(s2); /* Stop listening */
\r
9603 /* Prepare return value */
\r
9604 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9605 cp->kind = CPRcmd;
\r
9608 *pr = (ProcRef *) cp;
\r
9615 AddInputSource(ProcRef pr, int lineByLine,
\r
9616 InputCallback func, VOIDSTAR closure)
\r
9618 InputSource *is, *is2 = NULL;
\r
9619 ChildProc *cp = (ChildProc *) pr;
\r
9621 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9622 is->lineByLine = lineByLine;
\r
9624 is->closure = closure;
\r
9625 is->second = NULL;
\r
9626 is->next = is->buf;
\r
9627 if (pr == NoProc) {
\r
9628 is->kind = CPReal;
\r
9629 consoleInputSource = is;
\r
9631 is->kind = cp->kind;
\r
9633 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9634 we create all threads suspended so that the is->hThread variable can be
\r
9635 safely assigned, then let the threads start with ResumeThread.
\r
9637 switch (cp->kind) {
\r
9639 is->hFile = cp->hFrom;
\r
9640 cp->hFrom = NULL; /* now owned by InputThread */
\r
9642 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9643 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9647 is->hFile = cp->hFrom;
\r
9648 cp->hFrom = NULL; /* now owned by InputThread */
\r
9650 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9651 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9655 is->sock = cp->sock;
\r
9657 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9658 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9662 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9664 is->sock = cp->sock;
\r
9666 is2->sock = cp->sock2;
\r
9667 is2->second = is2;
\r
9669 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9670 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9672 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9673 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9677 if( is->hThread != NULL ) {
\r
9678 ResumeThread( is->hThread );
\r
9681 if( is2 != NULL && is2->hThread != NULL ) {
\r
9682 ResumeThread( is2->hThread );
\r
9686 return (InputSourceRef) is;
\r
9690 RemoveInputSource(InputSourceRef isr)
\r
9694 is = (InputSource *) isr;
\r
9695 is->hThread = NULL; /* tell thread to stop */
\r
9696 CloseHandle(is->hThread);
\r
9697 if (is->second != NULL) {
\r
9698 is->second->hThread = NULL;
\r
9699 CloseHandle(is->second->hThread);
\r
9703 int no_wrap(char *message, int count)
\r
9705 ConsoleOutput(message, count, FALSE);
\r
9710 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9713 int outCount = SOCKET_ERROR;
\r
9714 ChildProc *cp = (ChildProc *) pr;
\r
9715 static OVERLAPPED ovl;
\r
9716 static int line = 0;
\r
9720 if (appData.noJoin || !appData.useInternalWrap)
\r
9721 return no_wrap(message, count);
\r
9724 int width = get_term_width();
\r
9725 int len = wrap(NULL, message, count, width, &line);
\r
9726 char *msg = malloc(len);
\r
9730 return no_wrap(message, count);
\r
9733 dbgchk = wrap(msg, message, count, width, &line);
\r
9734 if (dbgchk != len && appData.debugMode)
\r
9735 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9736 ConsoleOutput(msg, len, FALSE);
\r
9743 if (ovl.hEvent == NULL) {
\r
9744 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9746 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9748 switch (cp->kind) {
\r
9751 outCount = send(cp->sock, message, count, 0);
\r
9752 if (outCount == SOCKET_ERROR) {
\r
9753 *outError = WSAGetLastError();
\r
9755 *outError = NO_ERROR;
\r
9760 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9761 &dOutCount, NULL)) {
\r
9762 *outError = NO_ERROR;
\r
9763 outCount = (int) dOutCount;
\r
9765 *outError = GetLastError();
\r
9770 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9771 &dOutCount, &ovl);
\r
9772 if (*outError == NO_ERROR) {
\r
9773 outCount = (int) dOutCount;
\r
9783 if(n != 0) Sleep(n);
\r
9787 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9790 /* Ignore delay, not implemented for WinBoard */
\r
9791 return OutputToProcess(pr, message, count, outError);
\r
9796 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9797 char *buf, int count, int error)
\r
9799 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9802 /* see wgamelist.c for Game List functions */
\r
9803 /* see wedittags.c for Edit Tags functions */
\r
9810 char buf[MSG_SIZ];
\r
9813 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9814 f = fopen(buf, "r");
\r
9816 ProcessICSInitScript(f);
\r
9826 StartAnalysisClock()
\r
9828 if (analysisTimerEvent) return;
\r
9829 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9830 (UINT) 2000, NULL);
\r
9834 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9836 highlightInfo.sq[0].x = fromX;
\r
9837 highlightInfo.sq[0].y = fromY;
\r
9838 highlightInfo.sq[1].x = toX;
\r
9839 highlightInfo.sq[1].y = toY;
\r
9845 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9846 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9850 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9852 premoveHighlightInfo.sq[0].x = fromX;
\r
9853 premoveHighlightInfo.sq[0].y = fromY;
\r
9854 premoveHighlightInfo.sq[1].x = toX;
\r
9855 premoveHighlightInfo.sq[1].y = toY;
\r
9859 ClearPremoveHighlights()
\r
9861 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9862 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9866 ShutDownFrontEnd()
\r
9868 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9869 DeleteClipboardTempFiles();
\r
9875 if (IsIconic(hwndMain))
\r
9876 ShowWindow(hwndMain, SW_RESTORE);
\r
9878 SetActiveWindow(hwndMain);
\r
9882 * Prototypes for animation support routines
\r
9884 static void ScreenSquare(int column, int row, POINT * pt);
\r
9885 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9886 POINT frames[], int * nFrames);
\r
9892 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9893 { // [HGM] atomic: animate blast wave
\r
9896 explodeInfo.fromX = fromX;
\r
9897 explodeInfo.fromY = fromY;
\r
9898 explodeInfo.toX = toX;
\r
9899 explodeInfo.toY = toY;
\r
9900 for(i=1; i<4*kFactor; i++) {
\r
9901 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9902 DrawPosition(FALSE, board);
\r
9903 Sleep(appData.animSpeed);
\r
9905 explodeInfo.radius = 0;
\r
9906 DrawPosition(TRUE, board);
\r
9910 AnimateMove(board, fromX, fromY, toX, toY)
\r
9917 ChessSquare piece;
\r
9918 POINT start, finish, mid;
\r
9919 POINT frames[kFactor * 2 + 1];
\r
9922 if (!appData.animate) return;
\r
9923 if (doingSizing) return;
\r
9924 if (fromY < 0 || fromX < 0) return;
\r
9925 piece = board[fromY][fromX];
\r
9926 if (piece >= EmptySquare) return;
\r
9928 ScreenSquare(fromX, fromY, &start);
\r
9929 ScreenSquare(toX, toY, &finish);
\r
9931 /* All moves except knight jumps move in straight line */
\r
9932 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9933 mid.x = start.x + (finish.x - start.x) / 2;
\r
9934 mid.y = start.y + (finish.y - start.y) / 2;
\r
9936 /* Knight: make straight movement then diagonal */
\r
9937 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9938 mid.x = start.x + (finish.x - start.x) / 2;
\r
9942 mid.y = start.y + (finish.y - start.y) / 2;
\r
9946 /* Don't use as many frames for very short moves */
\r
9947 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9948 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9950 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9952 animInfo.from.x = fromX;
\r
9953 animInfo.from.y = fromY;
\r
9954 animInfo.to.x = toX;
\r
9955 animInfo.to.y = toY;
\r
9956 animInfo.lastpos = start;
\r
9957 animInfo.piece = piece;
\r
9958 for (n = 0; n < nFrames; n++) {
\r
9959 animInfo.pos = frames[n];
\r
9960 DrawPosition(FALSE, NULL);
\r
9961 animInfo.lastpos = animInfo.pos;
\r
9962 Sleep(appData.animSpeed);
\r
9964 animInfo.pos = finish;
\r
9965 DrawPosition(FALSE, NULL);
\r
9966 animInfo.piece = EmptySquare;
\r
9967 Explode(board, fromX, fromY, toX, toY);
\r
9970 /* Convert board position to corner of screen rect and color */
\r
9973 ScreenSquare(column, row, pt)
\r
9974 int column; int row; POINT * pt;
\r
9977 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9978 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9980 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9981 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9985 /* Generate a series of frame coords from start->mid->finish.
\r
9986 The movement rate doubles until the half way point is
\r
9987 reached, then halves back down to the final destination,
\r
9988 which gives a nice slow in/out effect. The algorithmn
\r
9989 may seem to generate too many intermediates for short
\r
9990 moves, but remember that the purpose is to attract the
\r
9991 viewers attention to the piece about to be moved and
\r
9992 then to where it ends up. Too few frames would be less
\r
9996 Tween(start, mid, finish, factor, frames, nFrames)
\r
9997 POINT * start; POINT * mid;
\r
9998 POINT * finish; int factor;
\r
9999 POINT frames[]; int * nFrames;
\r
10001 int n, fraction = 1, count = 0;
\r
10003 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10004 for (n = 0; n < factor; n++)
\r
10006 for (n = 0; n < factor; n++) {
\r
10007 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10008 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10010 fraction = fraction / 2;
\r
10014 frames[count] = *mid;
\r
10017 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10019 for (n = 0; n < factor; n++) {
\r
10020 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10021 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10023 fraction = fraction * 2;
\r
10025 *nFrames = count;
\r
10029 SettingsPopUp(ChessProgramState *cps)
\r
10030 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10031 EngineOptionsPopup(savedHwnd, cps);
\r
10034 int flock(int fid, int code)
\r
10036 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10038 ov.hEvent = NULL;
\r
10040 ov.OffsetHigh = 0;
\r
10042 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10043 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10044 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10045 default: return -1;
\r
10054 static char col[8][20];
\r
10055 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10057 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10062 ActivateTheme (int new)
\r
10063 { // Redo initialization of features depending on options that can occur in themes
\r
10065 if(new) InitDrawingColors();
\r
10066 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10067 InitDrawingSizes(boardSize, 0);
\r
10068 InvalidateRect(hwndMain, NULL, TRUE);
\r