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
97 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 void mysrandom(unsigned int seed);
\r
102 extern int whiteFlag, blackFlag;
\r
103 Boolean flipClock = FALSE;
\r
104 extern HANDLE chatHandle[];
\r
105 extern enum ICS_TYPE ics_type;
\r
107 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
108 int MyGetFullPathName P((char *name, char *fullname));
\r
109 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
110 VOID NewVariantPopup(HWND hwnd);
\r
111 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
112 /*char*/int promoChar));
\r
113 void DisplayMove P((int moveNumber));
\r
114 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
115 void ChatPopUp P((char *s));
\r
117 ChessSquare piece;
\r
118 POINT pos; /* window coordinates of current pos */
\r
119 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
120 POINT from; /* board coordinates of the piece's orig pos */
\r
121 POINT to; /* board coordinates of the piece's new pos */
\r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
127 POINT start; /* window coordinates of start pos */
\r
128 POINT pos; /* window coordinates of current pos */
\r
129 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
130 POINT from; /* board coordinates of the piece's orig pos */
\r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
137 POINT sq[2]; /* board coordinates of from, to squares */
\r
140 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
145 typedef struct { // [HGM] atomic
\r
146 int fromX, fromY, toX, toY, radius;
\r
149 static ExplodeInfo explodeInfo;
\r
151 /* Window class names */
\r
152 char szAppName[] = "WinBoard";
\r
153 char szConsoleName[] = "WBConsole";
\r
155 /* Title bar text */
\r
156 char szTitle[] = "WinBoard";
\r
157 char szConsoleTitle[] = "I C S Interaction";
\r
160 char *settingsFileName;
\r
161 Boolean saveSettingsOnExit;
\r
162 char installDir[MSG_SIZ];
\r
163 int errorExitStatus;
\r
165 BoardSize boardSize;
\r
166 Boolean chessProgram;
\r
167 //static int boardX, boardY;
\r
168 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
169 int squareSize, lineGap, minorSize, border;
\r
170 static int winW, winH;
\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
172 static int logoHeight = 0;
\r
173 static char messageText[MESSAGE_TEXT_MAX];
\r
174 static int clockTimerEvent = 0;
\r
175 static int loadGameTimerEvent = 0;
\r
176 static int analysisTimerEvent = 0;
\r
177 static DelayedEventCallback delayedTimerCallback;
\r
178 static int delayedTimerEvent = 0;
\r
179 static int buttonCount = 2;
\r
180 char *icsTextMenuString;
\r
182 char *firstChessProgramNames;
\r
183 char *secondChessProgramNames;
\r
185 #define PALETTESIZE 256
\r
187 HINSTANCE hInst; /* current instance */
\r
188 Boolean alwaysOnTop = FALSE;
\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
191 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
193 ColorClass currentColorClass;
\r
195 static HWND savedHwnd;
\r
196 HWND hCommPort = NULL; /* currently open comm port */
\r
197 static HWND hwndPause; /* pause button */
\r
198 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
199 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
200 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
201 explodeBrush, /* [HGM] atomic */
\r
202 markerBrush, /* [HGM] markers */
\r
203 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
204 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
205 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
206 static HPEN gridPen = NULL;
\r
207 static HPEN highlightPen = NULL;
\r
208 static HPEN premovePen = NULL;
\r
209 static NPLOGPALETTE pLogPal;
\r
210 static BOOL paletteChanged = FALSE;
\r
211 static HICON iconWhite, iconBlack, iconCurrent;
\r
212 static int doingSizing = FALSE;
\r
213 static int lastSizing = 0;
\r
214 static int prevStderrPort;
\r
215 static HBITMAP userLogo;
\r
217 static HBITMAP liteBackTexture = NULL;
\r
218 static HBITMAP darkBackTexture = NULL;
\r
219 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
220 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
221 static int backTextureSquareSize = 0;
\r
222 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
224 #if __GNUC__ && !defined(_winmajor)
\r
225 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
230 #if defined(_winmajor)
\r
231 #define oldDialog (_winmajor < 4)
\r
233 #define oldDialog 0
\r
237 #define INTERNATIONAL
\r
239 #ifdef INTERNATIONAL
\r
240 # define _(s) T_(s)
\r
246 # define Translate(x, y)
\r
247 # define LoadLanguageFile(s)
\r
250 #ifdef INTERNATIONAL
\r
252 Boolean barbaric; // flag indicating if translation is needed
\r
254 // list of item numbers used in each dialog (used to alter language at run time)
\r
256 #define ABOUTBOX -1 /* not sure why these are needed */
\r
257 #define ABOUTBOX2 -1
\r
259 int dialogItems[][42] = {
\r
260 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
261 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
262 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
263 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
264 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
266 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
269 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
272 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
273 { ABOUTBOX2, IDC_ChessBoard },
\r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
275 OPT_GameListClose, IDC_GameListDoFilter },
\r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
277 { DLG_Error, IDOK },
\r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
279 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
282 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
283 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
284 { DLG_IndexNumber, IDC_Index },
\r
285 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
286 { DLG_TypeInName, IDOK, IDCANCEL },
\r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
288 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
290 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
291 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
292 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
293 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
294 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
295 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
297 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
298 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
299 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
300 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
301 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
302 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
303 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
304 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
306 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
307 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
308 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
309 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
310 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
311 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
312 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
314 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
315 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
316 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
317 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
318 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
319 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
320 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
321 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
323 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
324 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
325 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
326 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
329 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
330 { DLG_MoveHistory },
\r
331 { DLG_EvalGraph },
\r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
335 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
336 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
337 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
339 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
340 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
345 static int lastChecked;
\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
347 extern int tinyLayout;
\r
348 extern char * menuBarText[][10];
\r
351 LoadLanguageFile(char *name)
\r
352 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
354 int i=0, j=0, n=0, k;
\r
357 if(!name || name[0] == NULLCHAR) return;
\r
358 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
359 appData.language = oldLanguage;
\r
360 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
361 if((f = fopen(buf, "r")) == NULL) return;
\r
362 while((k = fgetc(f)) != EOF) {
\r
363 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
364 languageBuf[i] = k;
\r
366 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
368 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
369 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
370 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
371 english[j] = languageBuf + n + 1; *p = 0;
\r
372 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
378 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
380 case 'n': k = '\n'; break;
\r
381 case 'r': k = '\r'; break;
\r
382 case 't': k = '\t'; break;
\r
384 languageBuf[--i] = k;
\r
389 barbaric = (j != 0);
\r
390 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
395 { // return the translation of the given string
\r
396 // efficiency can be improved a lot...
\r
398 static char buf[MSG_SIZ];
\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
400 if(!barbaric) return s;
\r
401 if(!s) return ""; // sanity
\r
402 while(english[i]) {
\r
403 if(!strcmp(s, english[i])) return foreign[i];
\r
404 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
405 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
414 Translate(HWND hDlg, int dialogID)
\r
415 { // translate all text items in the given dialog
\r
417 char buf[MSG_SIZ], *s;
\r
418 if(!barbaric) return;
\r
419 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
420 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
421 GetWindowText( hDlg, buf, MSG_SIZ );
\r
423 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
424 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
425 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
426 if(strlen(buf) == 0) continue;
\r
428 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
433 TranslateOneMenu(int i, HMENU subMenu)
\r
436 static MENUITEMINFO info;
\r
438 info.cbSize = sizeof(MENUITEMINFO);
\r
439 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
440 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
442 info.dwTypeData = buf;
\r
443 info.cch = sizeof(buf);
\r
444 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
447 else menuText[i][j] = strdup(buf); // remember original on first change
\r
449 if(buf[0] == NULLCHAR) continue;
\r
450 info.dwTypeData = T_(buf);
\r
451 info.cch = strlen(buf)+1;
\r
452 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
458 TranslateMenus(int addLanguage)
\r
461 WIN32_FIND_DATA fileData;
\r
463 #define IDM_English 1970
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
467 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
468 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
469 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
470 TranslateOneMenu(i, subMenu);
\r
472 DrawMenuBar(hwndMain);
\r
475 if(!addLanguage) return;
\r
476 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
477 HMENU mainMenu = GetMenu(hwndMain);
\r
478 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
479 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
481 i = 0; lastChecked = IDM_English;
\r
483 char *p, *q = fileData.cFileName;
\r
484 int checkFlag = MF_UNCHECKED;
\r
485 languageFile[i] = strdup(q);
\r
486 if(barbaric && !strcmp(oldLanguage, q)) {
\r
487 checkFlag = MF_CHECKED;
\r
488 lastChecked = IDM_English + i + 1;
\r
489 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
491 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
492 p = strstr(fileData.cFileName, ".lng");
\r
494 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
495 } while(FindNextFile(hFind, &fileData));
\r
502 #define IDM_RecentEngines 3000
\r
505 RecentEngineMenu (char *s)
\r
507 if(appData.icsActive) return;
\r
508 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
509 HMENU mainMenu = GetMenu(hwndMain);
\r
510 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
511 int i=IDM_RecentEngines;
\r
512 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
513 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
515 char *p = strchr(s, '\n');
\r
516 if(p == NULL) return; // malformed!
\r
518 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
532 int cliWidth, cliHeight;
\r
535 SizeInfo sizeInfo[] =
\r
537 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
538 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
539 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
540 { "petite", 33, 1, 1, 1, 0, 0 },
\r
541 { "slim", 37, 2, 1, 0, 0, 0 },
\r
542 { "small", 40, 2, 1, 0, 0, 0 },
\r
543 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
544 { "middling", 49, 2, 0, 0, 0, 0 },
\r
545 { "average", 54, 2, 0, 0, 0, 0 },
\r
546 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
547 { "medium", 64, 3, 0, 0, 0, 0 },
\r
548 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
549 { "large", 80, 3, 0, 0, 0, 0 },
\r
550 { "big", 87, 3, 0, 0, 0, 0 },
\r
551 { "huge", 95, 3, 0, 0, 0, 0 },
\r
552 { "giant", 108, 3, 0, 0, 0, 0 },
\r
553 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
554 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
555 { NULL, 0, 0, 0, 0, 0, 0 }
\r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
577 { 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
578 { 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
581 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
591 #define N_BUTTONS 5
\r
593 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
595 {"<<", IDM_ToStart, NULL, NULL},
\r
596 {"<", IDM_Backward, NULL, NULL},
\r
597 {"P", IDM_Pause, NULL, NULL},
\r
598 {">", IDM_Forward, NULL, NULL},
\r
599 {">>", IDM_ToEnd, NULL, NULL},
\r
602 int tinyLayout = 0, smallLayout = 0;
\r
603 #define MENU_BAR_ITEMS 9
\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
605 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
606 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
610 MySound sounds[(int)NSoundClasses];
\r
611 MyTextAttribs textAttribs[(int)NColorClasses];
\r
613 MyColorizeAttribs colorizeAttribs[] = {
\r
614 { (COLORREF)0, 0, N_("Shout Text") },
\r
615 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
616 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
617 { (COLORREF)0, 0, N_("Channel Text") },
\r
618 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
619 { (COLORREF)0, 0, N_("Tell Text") },
\r
620 { (COLORREF)0, 0, N_("Challenge Text") },
\r
621 { (COLORREF)0, 0, N_("Request Text") },
\r
622 { (COLORREF)0, 0, N_("Seek Text") },
\r
623 { (COLORREF)0, 0, N_("Normal Text") },
\r
624 { (COLORREF)0, 0, N_("None") }
\r
629 static char *commentTitle;
\r
630 static char *commentText;
\r
631 static int commentIndex;
\r
632 static Boolean editComment = FALSE;
\r
635 char errorTitle[MSG_SIZ];
\r
636 char errorMessage[2*MSG_SIZ];
\r
637 HWND errorDialog = NULL;
\r
638 BOOLEAN moveErrorMessageUp = FALSE;
\r
639 BOOLEAN consoleEcho = TRUE;
\r
640 CHARFORMAT consoleCF;
\r
641 COLORREF consoleBackgroundColor;
\r
643 char *programVersion;
\r
649 typedef int CPKind;
\r
658 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
661 #define INPUT_SOURCE_BUF_SIZE 4096
\r
663 typedef struct _InputSource {
\r
670 char buf[INPUT_SOURCE_BUF_SIZE];
\r
674 InputCallback func;
\r
675 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
679 InputSource *consoleInputSource;
\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
685 VOID ConsoleCreate();
\r
687 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
692 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
694 void ParseIcsTextMenu(char *icsTextMenuString);
\r
695 VOID PopUpNameDialog(char firstchar);
\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
700 int GameListOptions();
\r
702 int dummy; // [HGM] for obsolete args
\r
704 HWND hwndMain = NULL; /* root window*/
\r
705 HWND hwndConsole = NULL;
\r
706 HWND commentDialog = NULL;
\r
707 HWND moveHistoryDialog = NULL;
\r
708 HWND evalGraphDialog = NULL;
\r
709 HWND engineOutputDialog = NULL;
\r
710 HWND gameListDialog = NULL;
\r
711 HWND editTagsDialog = NULL;
\r
713 int commentUp = FALSE;
\r
715 WindowPlacement wpMain;
\r
716 WindowPlacement wpConsole;
\r
717 WindowPlacement wpComment;
\r
718 WindowPlacement wpMoveHistory;
\r
719 WindowPlacement wpEvalGraph;
\r
720 WindowPlacement wpEngineOutput;
\r
721 WindowPlacement wpGameList;
\r
722 WindowPlacement wpTags;
\r
724 VOID EngineOptionsPopup(); // [HGM] settings
\r
726 VOID GothicPopUp(char *title, VariantClass variant);
\r
728 * Setting "frozen" should disable all user input other than deleting
\r
729 * the window. We do this while engines are initializing themselves.
\r
731 static int frozen = 0;
\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
738 if (frozen) return;
\r
740 hmenu = GetMenu(hwndMain);
\r
741 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
742 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
744 DrawMenuBar(hwndMain);
\r
747 /* Undo a FreezeUI */
\r
753 if (!frozen) return;
\r
755 hmenu = GetMenu(hwndMain);
\r
756 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
757 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
759 DrawMenuBar(hwndMain);
\r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
770 #define JAWS_ALT_INTERCEPT
\r
771 #define JAWS_KBUP_NAVIGATION
\r
772 #define JAWS_KBDOWN_NAVIGATION
\r
773 #define JAWS_MENU_ITEMS
\r
774 #define JAWS_SILENCE
\r
775 #define JAWS_REPLAY
\r
777 #define JAWS_COPYRIGHT
\r
778 #define JAWS_DELETE(X) X
\r
779 #define SAYMACHINEMOVE()
\r
783 /*---------------------------------------------------------------------------*\
\r
787 \*---------------------------------------------------------------------------*/
\r
790 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
791 LPSTR lpCmdLine, int nCmdShow)
\r
794 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
795 // INITCOMMONCONTROLSEX ex;
\r
799 LoadLibrary("RICHED32.DLL");
\r
800 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
802 if (!InitApplication(hInstance)) {
\r
805 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
812 // InitCommonControlsEx(&ex);
\r
813 InitCommonControls();
\r
815 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
816 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
817 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
819 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
821 while (GetMessage(&msg, /* message structure */
\r
822 NULL, /* handle of window receiving the message */
\r
823 0, /* lowest message to examine */
\r
824 0)) /* highest message to examine */
\r
827 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
828 // [HGM] navigate: switch between all windows with tab
\r
829 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
830 int i, currentElement = 0;
\r
832 // first determine what element of the chain we come from (if any)
\r
833 if(appData.icsActive) {
\r
834 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
835 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
837 if(engineOutputDialog && EngineOutputIsUp()) {
\r
838 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
839 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
841 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
842 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
844 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
845 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
846 if(msg.hwnd == e1) currentElement = 2; else
\r
847 if(msg.hwnd == e2) currentElement = 3; else
\r
848 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
849 if(msg.hwnd == mh) currentElement = 4; else
\r
850 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
851 if(msg.hwnd == hText) currentElement = 5; else
\r
852 if(msg.hwnd == hInput) currentElement = 6; else
\r
853 for (i = 0; i < N_BUTTONS; i++) {
\r
854 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
857 // determine where to go to
\r
858 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
860 currentElement = (currentElement + direction) % 7;
\r
861 switch(currentElement) {
\r
863 h = hwndMain; break; // passing this case always makes the loop exit
\r
865 h = buttonDesc[0].hwnd; break; // could be NULL
\r
867 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
870 if(!EngineOutputIsUp()) continue;
\r
873 if(!MoveHistoryIsUp()) continue;
\r
875 // case 6: // input to eval graph does not seem to get here!
\r
876 // if(!EvalGraphIsUp()) continue;
\r
877 // h = evalGraphDialog; break;
\r
879 if(!appData.icsActive) continue;
\r
883 if(!appData.icsActive) continue;
\r
889 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
890 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
893 continue; // this message now has been processed
\r
897 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
898 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
899 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
900 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
901 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
902 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
903 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
904 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
905 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
906 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
907 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
908 for(i=0; i<MAX_CHAT; i++)
\r
909 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
912 if(done) continue; // [HGM] chat: end patch
\r
913 TranslateMessage(&msg); /* Translates virtual key codes */
\r
914 DispatchMessage(&msg); /* Dispatches message to window */
\r
919 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
922 /*---------------------------------------------------------------------------*\
\r
924 * Initialization functions
\r
926 \*---------------------------------------------------------------------------*/
\r
930 { // update user logo if necessary
\r
931 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
933 if(appData.autoLogo) {
\r
934 curName = UserName();
\r
935 if(strcmp(curName, oldUserName)) {
\r
936 GetCurrentDirectory(MSG_SIZ, dir);
\r
937 SetCurrentDirectory(installDir);
\r
938 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
939 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
940 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
941 if(userLogo == NULL)
\r
942 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
943 SetCurrentDirectory(dir); /* return to prev directory */
\r
949 InitApplication(HINSTANCE hInstance)
\r
953 /* Fill in window class structure with parameters that describe the */
\r
956 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
957 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
958 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
959 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
960 wc.hInstance = hInstance; /* Owner of this class */
\r
961 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
962 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
963 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
964 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
965 wc.lpszClassName = szAppName; /* Name to register as */
\r
967 /* Register the window class and return success/failure code. */
\r
968 if (!RegisterClass(&wc)) return FALSE;
\r
970 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
971 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
973 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
974 wc.hInstance = hInstance;
\r
975 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
976 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
977 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
978 wc.lpszMenuName = NULL;
\r
979 wc.lpszClassName = szConsoleName;
\r
981 if (!RegisterClass(&wc)) return FALSE;
\r
986 /* Set by InitInstance, used by EnsureOnScreen */
\r
987 int screenHeight, screenWidth;
\r
988 RECT screenGeometry;
\r
991 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
993 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
994 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
995 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
996 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
997 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
998 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1002 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1004 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1005 GetCurrentDirectory(MSG_SIZ, dir);
\r
1006 SetCurrentDirectory(installDir);
\r
1007 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1008 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1010 if (cps->programLogo == NULL && appData.debugMode) {
\r
1011 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1013 } else if(appData.autoLogo) {
\r
1014 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1015 char *opponent = "";
\r
1016 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1017 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1018 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1019 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1020 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1021 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 if(appData.directory[n] && appData.directory[n][0]) {
\r
1025 SetCurrentDirectory(appData.directory[n]);
\r
1026 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1029 SetCurrentDirectory(dir); /* return to prev directory */
\r
1035 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1036 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1038 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1039 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1040 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1041 liteBackTextureMode = appData.liteBackTextureMode;
\r
1043 if (liteBackTexture == NULL && appData.debugMode) {
\r
1044 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1048 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1049 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1050 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1051 darkBackTextureMode = appData.darkBackTextureMode;
\r
1053 if (darkBackTexture == NULL && appData.debugMode) {
\r
1054 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1059 #ifndef SM_CXVIRTUALSCREEN
\r
1060 #define SM_CXVIRTUALSCREEN 78
\r
1062 #ifndef SM_CYVIRTUALSCREEN
\r
1063 #define SM_CYVIRTUALSCREEN 79
\r
1065 #ifndef SM_XVIRTUALSCREEN
\r
1066 #define SM_XVIRTUALSCREEN 76
\r
1068 #ifndef SM_YVIRTUALSCREEN
\r
1069 #define SM_YVIRTUALSCREEN 77
\r
1075 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1076 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1077 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1078 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1079 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1080 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1081 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1082 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1086 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1088 HWND hwnd; /* Main window handle. */
\r
1090 WINDOWPLACEMENT wp;
\r
1093 hInst = hInstance; /* Store instance handle in our global variable */
\r
1094 programName = szAppName;
\r
1096 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1097 *filepart = NULLCHAR;
\r
1098 SetCurrentDirectory(installDir);
\r
1100 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1102 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1104 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1105 /* xboard, and older WinBoards, controlled the move sound with the
\r
1106 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1107 always turn the option on (so that the backend will call us),
\r
1108 then let the user turn the sound off by setting it to silence if
\r
1109 desired. To accommodate old winboard.ini files saved by old
\r
1110 versions of WinBoard, we also turn off the sound if the option
\r
1111 was initially set to false. [HGM] taken out of InitAppData */
\r
1112 if (!appData.ringBellAfterMoves) {
\r
1113 sounds[(int)SoundMove].name = strdup("");
\r
1114 appData.ringBellAfterMoves = TRUE;
\r
1116 if (appData.debugMode) {
\r
1117 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1118 setbuf(debugFP, NULL);
\r
1121 LoadLanguageFile(appData.language);
\r
1125 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1126 // InitEngineUCI( installDir, &second );
\r
1128 /* Create a main window for this application instance. */
\r
1129 hwnd = CreateWindow(szAppName, szTitle,
\r
1130 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1131 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1132 NULL, NULL, hInstance, NULL);
\r
1135 /* If window could not be created, return "failure" */
\r
1140 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1141 LoadLogo(&first, 0, FALSE);
\r
1142 LoadLogo(&second, 1, appData.icsActive);
\r
1146 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1147 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1148 iconCurrent = iconWhite;
\r
1149 InitDrawingColors();
\r
1151 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1152 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1153 /* Compute window size for each board size, and use the largest
\r
1154 size that fits on this screen as the default. */
\r
1155 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1156 if (boardSize == (BoardSize)-1 &&
\r
1157 winH <= screenHeight
\r
1158 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1159 && winW <= screenWidth) {
\r
1160 boardSize = (BoardSize)ibs;
\r
1164 InitDrawingSizes(boardSize, 0);
\r
1165 RecentEngineMenu(appData.recentEngineList);
\r
1167 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1169 /* [AS] Load textures if specified */
\r
1172 mysrandom( (unsigned) time(NULL) );
\r
1174 /* [AS] Restore layout */
\r
1175 if( wpMoveHistory.visible ) {
\r
1176 MoveHistoryPopUp();
\r
1179 if( wpEvalGraph.visible ) {
\r
1183 if( wpEngineOutput.visible ) {
\r
1184 EngineOutputPopUp();
\r
1187 /* Make the window visible; update its client area; and return "success" */
\r
1188 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1189 wp.length = sizeof(WINDOWPLACEMENT);
\r
1191 wp.showCmd = nCmdShow;
\r
1192 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1193 wp.rcNormalPosition.left = wpMain.x;
\r
1194 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1195 wp.rcNormalPosition.top = wpMain.y;
\r
1196 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1197 SetWindowPlacement(hwndMain, &wp);
\r
1199 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1201 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1202 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1204 if (hwndConsole) {
\r
1206 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1207 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1209 ShowWindow(hwndConsole, nCmdShow);
\r
1210 SetActiveWindow(hwndConsole);
\r
1212 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1213 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1222 HMENU hmenu = GetMenu(hwndMain);
\r
1224 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1225 MF_BYCOMMAND|((appData.icsActive &&
\r
1226 *appData.icsCommPort != NULLCHAR) ?
\r
1227 MF_ENABLED : MF_GRAYED));
\r
1228 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1229 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1230 MF_CHECKED : MF_UNCHECKED));
\r
1233 //---------------------------------------------------------------------------------------------------------
\r
1235 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1236 #define XBOARD FALSE
\r
1238 #define OPTCHAR "/"
\r
1239 #define SEPCHAR "="
\r
1240 #define TOPLEVEL 0
\r
1244 // front-end part of option handling
\r
1247 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1249 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1250 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1253 lf->lfEscapement = 0;
\r
1254 lf->lfOrientation = 0;
\r
1255 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1256 lf->lfItalic = mfp->italic;
\r
1257 lf->lfUnderline = mfp->underline;
\r
1258 lf->lfStrikeOut = mfp->strikeout;
\r
1259 lf->lfCharSet = mfp->charset;
\r
1260 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1261 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1262 lf->lfQuality = DEFAULT_QUALITY;
\r
1263 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1264 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1268 CreateFontInMF(MyFont *mf)
\r
1270 LFfromMFP(&mf->lf, &mf->mfp);
\r
1271 if (mf->hf) DeleteObject(mf->hf);
\r
1272 mf->hf = CreateFontIndirect(&mf->lf);
\r
1275 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1277 colorVariable[] = {
\r
1278 &whitePieceColor,
\r
1279 &blackPieceColor,
\r
1280 &lightSquareColor,
\r
1281 &darkSquareColor,
\r
1282 &highlightSquareColor,
\r
1283 &premoveHighlightColor,
\r
1285 &consoleBackgroundColor,
\r
1286 &appData.fontForeColorWhite,
\r
1287 &appData.fontBackColorWhite,
\r
1288 &appData.fontForeColorBlack,
\r
1289 &appData.fontBackColorBlack,
\r
1290 &appData.evalHistColorWhite,
\r
1291 &appData.evalHistColorBlack,
\r
1292 &appData.highlightArrowColor,
\r
1295 /* Command line font name parser. NULL name means do nothing.
\r
1296 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1297 For backward compatibility, syntax without the colon is also
\r
1298 accepted, but font names with digits in them won't work in that case.
\r
1301 ParseFontName(char *name, MyFontParams *mfp)
\r
1304 if (name == NULL) return;
\r
1306 q = strchr(p, ':');
\r
1308 if (q - p >= sizeof(mfp->faceName))
\r
1309 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1310 memcpy(mfp->faceName, p, q - p);
\r
1311 mfp->faceName[q - p] = NULLCHAR;
\r
1314 q = mfp->faceName;
\r
1316 while (*p && !isdigit(*p)) {
\r
1318 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1319 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1321 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1324 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1325 mfp->pointSize = (float) atof(p);
\r
1326 mfp->bold = (strchr(p, 'b') != NULL);
\r
1327 mfp->italic = (strchr(p, 'i') != NULL);
\r
1328 mfp->underline = (strchr(p, 'u') != NULL);
\r
1329 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1330 mfp->charset = DEFAULT_CHARSET;
\r
1331 q = strchr(p, 'c');
\r
1333 mfp->charset = (BYTE) atoi(q+1);
\r
1337 ParseFont(char *name, int number)
\r
1338 { // wrapper to shield back-end from 'font'
\r
1339 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1344 { // in WB we have a 2D array of fonts; this initializes their description
\r
1346 /* Point font array elements to structures and
\r
1347 parse default font names */
\r
1348 for (i=0; i<NUM_FONTS; i++) {
\r
1349 for (j=0; j<NUM_SIZES; j++) {
\r
1350 font[j][i] = &fontRec[j][i];
\r
1351 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1358 { // here we create the actual fonts from the selected descriptions
\r
1360 for (i=0; i<NUM_FONTS; i++) {
\r
1361 for (j=0; j<NUM_SIZES; j++) {
\r
1362 CreateFontInMF(font[j][i]);
\r
1366 /* Color name parser.
\r
1367 X version accepts X color names, but this one
\r
1368 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1370 ParseColorName(char *name)
\r
1372 int red, green, blue, count;
\r
1373 char buf[MSG_SIZ];
\r
1375 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1377 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1378 &red, &green, &blue);
\r
1381 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1382 DisplayError(buf, 0);
\r
1383 return RGB(0, 0, 0);
\r
1385 return PALETTERGB(red, green, blue);
\r
1389 ParseColor(int n, char *name)
\r
1390 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1391 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1395 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1397 char *e = argValue;
\r
1401 if (*e == 'b') eff |= CFE_BOLD;
\r
1402 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1403 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1404 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1405 else if (*e == '#' || isdigit(*e)) break;
\r
1409 *color = ParseColorName(e);
\r
1413 ParseTextAttribs(ColorClass cc, char *s)
\r
1414 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1415 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1416 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1420 ParseBoardSize(void *addr, char *name)
\r
1421 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1422 BoardSize bs = SizeTiny;
\r
1423 while (sizeInfo[bs].name != NULL) {
\r
1424 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1425 *(BoardSize *)addr = bs;
\r
1430 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1435 { // [HGM] import name from appData first
\r
1438 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1439 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1440 textAttribs[cc].sound.data = NULL;
\r
1441 MyLoadSound(&textAttribs[cc].sound);
\r
1443 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1444 textAttribs[cc].sound.name = strdup("");
\r
1445 textAttribs[cc].sound.data = NULL;
\r
1447 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1448 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1449 sounds[sc].data = NULL;
\r
1450 MyLoadSound(&sounds[sc]);
\r
1455 SetCommPortDefaults()
\r
1457 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1458 dcb.DCBlength = sizeof(DCB);
\r
1459 dcb.BaudRate = 9600;
\r
1460 dcb.fBinary = TRUE;
\r
1461 dcb.fParity = FALSE;
\r
1462 dcb.fOutxCtsFlow = FALSE;
\r
1463 dcb.fOutxDsrFlow = FALSE;
\r
1464 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1465 dcb.fDsrSensitivity = FALSE;
\r
1466 dcb.fTXContinueOnXoff = TRUE;
\r
1467 dcb.fOutX = FALSE;
\r
1469 dcb.fNull = FALSE;
\r
1470 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1471 dcb.fAbortOnError = FALSE;
\r
1473 dcb.Parity = SPACEPARITY;
\r
1474 dcb.StopBits = ONESTOPBIT;
\r
1477 // [HGM] args: these three cases taken out to stay in front-end
\r
1479 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1480 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1481 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1482 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1484 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1485 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1486 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1487 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1488 ad->argName, mfp->faceName, mfp->pointSize,
\r
1489 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1490 mfp->bold ? "b" : "",
\r
1491 mfp->italic ? "i" : "",
\r
1492 mfp->underline ? "u" : "",
\r
1493 mfp->strikeout ? "s" : "",
\r
1494 (int)mfp->charset);
\r
1500 { // [HGM] copy the names from the internal WB variables to appData
\r
1503 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1504 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1505 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1506 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1510 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1511 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1512 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1513 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1514 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1515 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1516 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1517 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1518 (ta->effects) ? " " : "",
\r
1519 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1523 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1524 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1525 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1526 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1527 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1531 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1532 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1533 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1537 ParseCommPortSettings(char *s)
\r
1538 { // wrapper to keep dcb from back-end
\r
1539 ParseCommSettings(s, &dcb);
\r
1544 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1545 GetActualPlacement(hwndMain, &wpMain);
\r
1546 GetActualPlacement(hwndConsole, &wpConsole);
\r
1547 GetActualPlacement(commentDialog, &wpComment);
\r
1548 GetActualPlacement(editTagsDialog, &wpTags);
\r
1549 GetActualPlacement(gameListDialog, &wpGameList);
\r
1550 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1551 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1552 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1556 PrintCommPortSettings(FILE *f, char *name)
\r
1557 { // wrapper to shield back-end from DCB
\r
1558 PrintCommSettings(f, name, &dcb);
\r
1562 MySearchPath(char *installDir, char *name, char *fullname)
\r
1564 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1565 if(name[0]== '%') {
\r
1566 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1567 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1568 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1569 *strchr(buf, '%') = 0;
\r
1570 strcat(fullname, getenv(buf));
\r
1571 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1573 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1574 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1575 return (int) strlen(fullname);
\r
1577 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1581 MyGetFullPathName(char *name, char *fullname)
\r
1584 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1589 { // [HGM] args: allows testing if main window is realized from back-end
\r
1590 return hwndMain != NULL;
\r
1594 PopUpStartupDialog()
\r
1598 LoadLanguageFile(appData.language);
\r
1599 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1600 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1601 FreeProcInstance(lpProc);
\r
1604 /*---------------------------------------------------------------------------*\
\r
1606 * GDI board drawing routines
\r
1608 \*---------------------------------------------------------------------------*/
\r
1610 /* [AS] Draw square using background texture */
\r
1611 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1616 return; /* Should never happen! */
\r
1619 SetGraphicsMode( dst, GM_ADVANCED );
\r
1626 /* X reflection */
\r
1631 x.eDx = (FLOAT) dw + dx - 1;
\r
1634 SetWorldTransform( dst, &x );
\r
1637 /* Y reflection */
\r
1643 x.eDy = (FLOAT) dh + dy - 1;
\r
1645 SetWorldTransform( dst, &x );
\r
1653 x.eDx = (FLOAT) dx;
\r
1654 x.eDy = (FLOAT) dy;
\r
1657 SetWorldTransform( dst, &x );
\r
1661 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1669 SetWorldTransform( dst, &x );
\r
1671 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1674 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1676 PM_WP = (int) WhitePawn,
\r
1677 PM_WN = (int) WhiteKnight,
\r
1678 PM_WB = (int) WhiteBishop,
\r
1679 PM_WR = (int) WhiteRook,
\r
1680 PM_WQ = (int) WhiteQueen,
\r
1681 PM_WF = (int) WhiteFerz,
\r
1682 PM_WW = (int) WhiteWazir,
\r
1683 PM_WE = (int) WhiteAlfil,
\r
1684 PM_WM = (int) WhiteMan,
\r
1685 PM_WO = (int) WhiteCannon,
\r
1686 PM_WU = (int) WhiteUnicorn,
\r
1687 PM_WH = (int) WhiteNightrider,
\r
1688 PM_WA = (int) WhiteAngel,
\r
1689 PM_WC = (int) WhiteMarshall,
\r
1690 PM_WAB = (int) WhiteCardinal,
\r
1691 PM_WD = (int) WhiteDragon,
\r
1692 PM_WL = (int) WhiteLance,
\r
1693 PM_WS = (int) WhiteCobra,
\r
1694 PM_WV = (int) WhiteFalcon,
\r
1695 PM_WSG = (int) WhiteSilver,
\r
1696 PM_WG = (int) WhiteGrasshopper,
\r
1697 PM_WK = (int) WhiteKing,
\r
1698 PM_BP = (int) BlackPawn,
\r
1699 PM_BN = (int) BlackKnight,
\r
1700 PM_BB = (int) BlackBishop,
\r
1701 PM_BR = (int) BlackRook,
\r
1702 PM_BQ = (int) BlackQueen,
\r
1703 PM_BF = (int) BlackFerz,
\r
1704 PM_BW = (int) BlackWazir,
\r
1705 PM_BE = (int) BlackAlfil,
\r
1706 PM_BM = (int) BlackMan,
\r
1707 PM_BO = (int) BlackCannon,
\r
1708 PM_BU = (int) BlackUnicorn,
\r
1709 PM_BH = (int) BlackNightrider,
\r
1710 PM_BA = (int) BlackAngel,
\r
1711 PM_BC = (int) BlackMarshall,
\r
1712 PM_BG = (int) BlackGrasshopper,
\r
1713 PM_BAB = (int) BlackCardinal,
\r
1714 PM_BD = (int) BlackDragon,
\r
1715 PM_BL = (int) BlackLance,
\r
1716 PM_BS = (int) BlackCobra,
\r
1717 PM_BV = (int) BlackFalcon,
\r
1718 PM_BSG = (int) BlackSilver,
\r
1719 PM_BK = (int) BlackKing
\r
1722 static HFONT hPieceFont = NULL;
\r
1723 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1724 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1725 static int fontBitmapSquareSize = 0;
\r
1726 static char pieceToFontChar[(int) EmptySquare] =
\r
1727 { 'p', 'n', 'b', 'r', 'q',
\r
1728 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1729 'k', 'o', 'm', 'v', 't', 'w',
\r
1730 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1733 extern BOOL SetCharTable( char *table, const char * map );
\r
1734 /* [HGM] moved to backend.c */
\r
1736 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1739 BYTE r1 = GetRValue( color );
\r
1740 BYTE g1 = GetGValue( color );
\r
1741 BYTE b1 = GetBValue( color );
\r
1747 /* Create a uniform background first */
\r
1748 hbrush = CreateSolidBrush( color );
\r
1749 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1750 FillRect( hdc, &rc, hbrush );
\r
1751 DeleteObject( hbrush );
\r
1754 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1755 int steps = squareSize / 2;
\r
1758 for( i=0; i<steps; i++ ) {
\r
1759 BYTE r = r1 - (r1-r2) * i / steps;
\r
1760 BYTE g = g1 - (g1-g2) * i / steps;
\r
1761 BYTE b = b1 - (b1-b2) * i / steps;
\r
1763 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1764 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1765 FillRect( hdc, &rc, hbrush );
\r
1766 DeleteObject(hbrush);
\r
1769 else if( mode == 2 ) {
\r
1770 /* Diagonal gradient, good more or less for every piece */
\r
1771 POINT triangle[3];
\r
1772 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1773 HBRUSH hbrush_old;
\r
1774 int steps = squareSize;
\r
1777 triangle[0].x = squareSize - steps;
\r
1778 triangle[0].y = squareSize;
\r
1779 triangle[1].x = squareSize;
\r
1780 triangle[1].y = squareSize;
\r
1781 triangle[2].x = squareSize;
\r
1782 triangle[2].y = squareSize - steps;
\r
1784 for( i=0; i<steps; i++ ) {
\r
1785 BYTE r = r1 - (r1-r2) * i / steps;
\r
1786 BYTE g = g1 - (g1-g2) * i / steps;
\r
1787 BYTE b = b1 - (b1-b2) * i / steps;
\r
1789 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1790 hbrush_old = SelectObject( hdc, hbrush );
\r
1791 Polygon( hdc, triangle, 3 );
\r
1792 SelectObject( hdc, hbrush_old );
\r
1793 DeleteObject(hbrush);
\r
1798 SelectObject( hdc, hpen );
\r
1803 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1804 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1805 piece: follow the steps as explained below.
\r
1807 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1811 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1815 int backColor = whitePieceColor;
\r
1816 int foreColor = blackPieceColor;
\r
1818 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1819 backColor = appData.fontBackColorWhite;
\r
1820 foreColor = appData.fontForeColorWhite;
\r
1822 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1823 backColor = appData.fontBackColorBlack;
\r
1824 foreColor = appData.fontForeColorBlack;
\r
1828 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1830 hbm_old = SelectObject( hdc, hbm );
\r
1834 rc.right = squareSize;
\r
1835 rc.bottom = squareSize;
\r
1837 /* Step 1: background is now black */
\r
1838 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1840 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1842 pt.x = (squareSize - sz.cx) / 2;
\r
1843 pt.y = (squareSize - sz.cy) / 2;
\r
1845 SetBkMode( hdc, TRANSPARENT );
\r
1846 SetTextColor( hdc, chroma );
\r
1847 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1848 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1850 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1851 /* Step 3: the area outside the piece is filled with white */
\r
1852 // FloodFill( hdc, 0, 0, chroma );
\r
1853 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1854 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1855 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1856 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1857 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1859 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1860 but if the start point is not inside the piece we're lost!
\r
1861 There should be a better way to do this... if we could create a region or path
\r
1862 from the fill operation we would be fine for example.
\r
1864 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1865 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1867 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1868 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1869 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1871 SelectObject( dc2, bm2 );
\r
1872 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1873 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1874 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1875 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1876 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1879 DeleteObject( bm2 );
\r
1882 SetTextColor( hdc, 0 );
\r
1884 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1885 draw the piece again in black for safety.
\r
1887 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1889 SelectObject( hdc, hbm_old );
\r
1891 if( hPieceMask[index] != NULL ) {
\r
1892 DeleteObject( hPieceMask[index] );
\r
1895 hPieceMask[index] = hbm;
\r
1898 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1900 SelectObject( hdc, hbm );
\r
1903 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1904 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1905 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1907 SelectObject( dc1, hPieceMask[index] );
\r
1908 SelectObject( dc2, bm2 );
\r
1909 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1910 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1913 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1914 the piece background and deletes (makes transparent) the rest.
\r
1915 Thanks to that mask, we are free to paint the background with the greates
\r
1916 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1917 We use this, to make gradients and give the pieces a "roundish" look.
\r
1919 SetPieceBackground( hdc, backColor, 2 );
\r
1920 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1924 DeleteObject( bm2 );
\r
1927 SetTextColor( hdc, foreColor );
\r
1928 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1930 SelectObject( hdc, hbm_old );
\r
1932 if( hPieceFace[index] != NULL ) {
\r
1933 DeleteObject( hPieceFace[index] );
\r
1936 hPieceFace[index] = hbm;
\r
1939 static int TranslatePieceToFontPiece( int piece )
\r
1969 case BlackMarshall:
\r
1973 case BlackNightrider:
\r
1979 case BlackUnicorn:
\r
1983 case BlackGrasshopper:
\r
1995 case BlackCardinal:
\r
2002 case WhiteMarshall:
\r
2006 case WhiteNightrider:
\r
2012 case WhiteUnicorn:
\r
2016 case WhiteGrasshopper:
\r
2028 case WhiteCardinal:
\r
2037 void CreatePiecesFromFont()
\r
2040 HDC hdc_window = NULL;
\r
2046 if( fontBitmapSquareSize < 0 ) {
\r
2047 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2051 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2052 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2053 fontBitmapSquareSize = -1;
\r
2057 if( fontBitmapSquareSize != squareSize ) {
\r
2058 hdc_window = GetDC( hwndMain );
\r
2059 hdc = CreateCompatibleDC( hdc_window );
\r
2061 if( hPieceFont != NULL ) {
\r
2062 DeleteObject( hPieceFont );
\r
2065 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2066 hPieceMask[i] = NULL;
\r
2067 hPieceFace[i] = NULL;
\r
2073 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2074 fontHeight = appData.fontPieceSize;
\r
2077 fontHeight = (fontHeight * squareSize) / 100;
\r
2079 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2081 lf.lfEscapement = 0;
\r
2082 lf.lfOrientation = 0;
\r
2083 lf.lfWeight = FW_NORMAL;
\r
2085 lf.lfUnderline = 0;
\r
2086 lf.lfStrikeOut = 0;
\r
2087 lf.lfCharSet = DEFAULT_CHARSET;
\r
2088 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2089 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2090 lf.lfQuality = PROOF_QUALITY;
\r
2091 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2092 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2093 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2095 hPieceFont = CreateFontIndirect( &lf );
\r
2097 if( hPieceFont == NULL ) {
\r
2098 fontBitmapSquareSize = -2;
\r
2101 /* Setup font-to-piece character table */
\r
2102 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2103 /* No (or wrong) global settings, try to detect the font */
\r
2104 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2106 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2108 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2109 /* DiagramTT* family */
\r
2110 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2112 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2113 /* Fairy symbols */
\r
2114 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2116 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2117 /* Good Companion (Some characters get warped as literal :-( */
\r
2118 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2119 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2120 SetCharTable(pieceToFontChar, s);
\r
2123 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2124 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2128 /* Create bitmaps */
\r
2129 hfont_old = SelectObject( hdc, hPieceFont );
\r
2130 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2131 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2132 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2134 SelectObject( hdc, hfont_old );
\r
2136 fontBitmapSquareSize = squareSize;
\r
2140 if( hdc != NULL ) {
\r
2144 if( hdc_window != NULL ) {
\r
2145 ReleaseDC( hwndMain, hdc_window );
\r
2150 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2152 char name[128], buf[MSG_SIZ];
\r
2154 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2155 if(appData.pieceDirectory[0]) {
\r
2157 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2158 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2159 if(res) return res;
\r
2161 if (gameInfo.event &&
\r
2162 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2163 strcmp(name, "k80s") == 0) {
\r
2164 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2166 return LoadBitmap(hinst, name);
\r
2170 /* Insert a color into the program's logical palette
\r
2171 structure. This code assumes the given color is
\r
2172 the result of the RGB or PALETTERGB macro, and it
\r
2173 knows how those macros work (which is documented).
\r
2176 InsertInPalette(COLORREF color)
\r
2178 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2180 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2181 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2182 pLogPal->palNumEntries--;
\r
2186 pe->peFlags = (char) 0;
\r
2187 pe->peRed = (char) (0xFF & color);
\r
2188 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2189 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2195 InitDrawingColors()
\r
2197 if (pLogPal == NULL) {
\r
2198 /* Allocate enough memory for a logical palette with
\r
2199 * PALETTESIZE entries and set the size and version fields
\r
2200 * of the logical palette structure.
\r
2202 pLogPal = (NPLOGPALETTE)
\r
2203 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2204 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2205 pLogPal->palVersion = 0x300;
\r
2207 pLogPal->palNumEntries = 0;
\r
2209 InsertInPalette(lightSquareColor);
\r
2210 InsertInPalette(darkSquareColor);
\r
2211 InsertInPalette(whitePieceColor);
\r
2212 InsertInPalette(blackPieceColor);
\r
2213 InsertInPalette(highlightSquareColor);
\r
2214 InsertInPalette(premoveHighlightColor);
\r
2216 /* create a logical color palette according the information
\r
2217 * in the LOGPALETTE structure.
\r
2219 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2221 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2222 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2223 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2224 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2225 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2226 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2227 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2228 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2229 /* [AS] Force rendering of the font-based pieces */
\r
2230 if( fontBitmapSquareSize > 0 ) {
\r
2231 fontBitmapSquareSize = 0;
\r
2237 BoardWidth(int boardSize, int n)
\r
2238 { /* [HGM] argument n added to allow different width and height */
\r
2239 int lineGap = sizeInfo[boardSize].lineGap;
\r
2241 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2242 lineGap = appData.overrideLineGap;
\r
2245 return (n + 1) * lineGap +
\r
2246 n * sizeInfo[boardSize].squareSize;
\r
2249 /* Respond to board resize by dragging edge */
\r
2251 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2253 BoardSize newSize = NUM_SIZES - 1;
\r
2254 static int recurse = 0;
\r
2255 if (IsIconic(hwndMain)) return;
\r
2256 if (recurse > 0) return;
\r
2258 while (newSize > 0) {
\r
2259 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2260 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2261 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2264 boardSize = newSize;
\r
2265 InitDrawingSizes(boardSize, flags);
\r
2270 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2273 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2275 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2276 ChessSquare piece;
\r
2277 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2279 SIZE clockSize, messageSize;
\r
2281 char buf[MSG_SIZ];
\r
2283 HMENU hmenu = GetMenu(hwndMain);
\r
2284 RECT crect, wrect, oldRect;
\r
2286 LOGBRUSH logbrush;
\r
2287 VariantClass v = gameInfo.variant;
\r
2289 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2290 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2292 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2293 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2294 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2295 oldBoardSize = boardSize;
\r
2297 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2298 { // correct board size to one where built-in pieces exist
\r
2299 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2300 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2301 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2302 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2303 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2304 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2305 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2306 boardSize = SizeMiddling;
\r
2309 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2311 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2312 oldRect.top = wpMain.y;
\r
2313 oldRect.right = wpMain.x + wpMain.width;
\r
2314 oldRect.bottom = wpMain.y + wpMain.height;
\r
2316 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2317 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2318 squareSize = sizeInfo[boardSize].squareSize;
\r
2319 lineGap = sizeInfo[boardSize].lineGap;
\r
2320 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2321 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2323 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2324 lineGap = appData.overrideLineGap;
\r
2327 if (tinyLayout != oldTinyLayout) {
\r
2328 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2330 style &= ~WS_SYSMENU;
\r
2331 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2332 "&Minimize\tCtrl+F4");
\r
2334 style |= WS_SYSMENU;
\r
2335 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2337 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2339 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2340 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2341 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2343 DrawMenuBar(hwndMain);
\r
2346 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2347 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2349 /* Get text area sizes */
\r
2350 hdc = GetDC(hwndMain);
\r
2351 if (appData.clockMode) {
\r
2352 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2354 snprintf(buf, MSG_SIZ, _("White"));
\r
2356 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2357 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2358 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2359 str = _("We only care about the height here");
\r
2360 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2361 SelectObject(hdc, oldFont);
\r
2362 ReleaseDC(hwndMain, hdc);
\r
2364 /* Compute where everything goes */
\r
2365 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2366 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2367 logoHeight = 2*clockSize.cy;
\r
2368 leftLogoRect.left = OUTER_MARGIN;
\r
2369 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2370 leftLogoRect.top = OUTER_MARGIN;
\r
2371 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2373 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2374 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2375 rightLogoRect.top = OUTER_MARGIN;
\r
2376 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2379 whiteRect.left = leftLogoRect.right;
\r
2380 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2381 whiteRect.top = OUTER_MARGIN;
\r
2382 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2384 blackRect.right = rightLogoRect.left;
\r
2385 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2386 blackRect.top = whiteRect.top;
\r
2387 blackRect.bottom = whiteRect.bottom;
\r
2389 whiteRect.left = OUTER_MARGIN;
\r
2390 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2391 whiteRect.top = OUTER_MARGIN;
\r
2392 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2394 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2395 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2396 blackRect.top = whiteRect.top;
\r
2397 blackRect.bottom = whiteRect.bottom;
\r
2399 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2402 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2403 if (appData.showButtonBar) {
\r
2404 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2405 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2407 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2409 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2410 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2412 boardRect.left = OUTER_MARGIN;
\r
2413 boardRect.right = boardRect.left + boardWidth;
\r
2414 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2415 boardRect.bottom = boardRect.top + boardHeight;
\r
2417 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2418 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2419 oldTinyLayout = tinyLayout;
\r
2420 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2421 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2422 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2423 winW *= 1 + twoBoards;
\r
2424 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2425 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2426 wpMain.height = winH; // without disturbing window attachments
\r
2427 GetWindowRect(hwndMain, &wrect);
\r
2428 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2429 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2431 // [HGM] placement: let attached windows follow size change.
\r
2432 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2433 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2434 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2435 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2436 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2438 /* compensate if menu bar wrapped */
\r
2439 GetClientRect(hwndMain, &crect);
\r
2440 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2441 wpMain.height += offby;
\r
2443 case WMSZ_TOPLEFT:
\r
2444 SetWindowPos(hwndMain, NULL,
\r
2445 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2446 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2449 case WMSZ_TOPRIGHT:
\r
2451 SetWindowPos(hwndMain, NULL,
\r
2452 wrect.left, wrect.bottom - wpMain.height,
\r
2453 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2456 case WMSZ_BOTTOMLEFT:
\r
2458 SetWindowPos(hwndMain, NULL,
\r
2459 wrect.right - wpMain.width, wrect.top,
\r
2460 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2463 case WMSZ_BOTTOMRIGHT:
\r
2467 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2468 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2473 for (i = 0; i < N_BUTTONS; i++) {
\r
2474 if (buttonDesc[i].hwnd != NULL) {
\r
2475 DestroyWindow(buttonDesc[i].hwnd);
\r
2476 buttonDesc[i].hwnd = NULL;
\r
2478 if (appData.showButtonBar) {
\r
2479 buttonDesc[i].hwnd =
\r
2480 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2481 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2482 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2483 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2484 (HMENU) buttonDesc[i].id,
\r
2485 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2487 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2488 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2489 MAKELPARAM(FALSE, 0));
\r
2491 if (buttonDesc[i].id == IDM_Pause)
\r
2492 hwndPause = buttonDesc[i].hwnd;
\r
2493 buttonDesc[i].wndproc = (WNDPROC)
\r
2494 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2497 if (gridPen != NULL) DeleteObject(gridPen);
\r
2498 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2499 if (premovePen != NULL) DeleteObject(premovePen);
\r
2500 if (lineGap != 0) {
\r
2501 logbrush.lbStyle = BS_SOLID;
\r
2502 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2504 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2505 lineGap, &logbrush, 0, NULL);
\r
2506 logbrush.lbColor = highlightSquareColor;
\r
2508 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2509 lineGap, &logbrush, 0, NULL);
\r
2511 logbrush.lbColor = premoveHighlightColor;
\r
2513 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2514 lineGap, &logbrush, 0, NULL);
\r
2516 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2517 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2518 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2519 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2520 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2521 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2522 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2523 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2525 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2526 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2527 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2528 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2529 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2530 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2531 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2532 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2536 /* [HGM] Licensing requirement */
\r
2538 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2541 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2543 GothicPopUp( "", VariantNormal);
\r
2546 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2548 /* Load piece bitmaps for this board size */
\r
2549 for (i=0; i<=2; i++) {
\r
2550 for (piece = WhitePawn;
\r
2551 (int) piece < (int) BlackPawn;
\r
2552 piece = (ChessSquare) ((int) piece + 1)) {
\r
2553 if (pieceBitmap[i][piece] != NULL)
\r
2554 DeleteObject(pieceBitmap[i][piece]);
\r
2558 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2559 // Orthodox Chess pieces
\r
2560 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2561 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2562 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2563 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2564 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2565 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2566 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2567 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2568 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2569 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2570 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2571 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2572 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2573 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2574 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2575 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2576 // in Shogi, Hijack the unused Queen for Lance
\r
2577 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2586 if(squareSize <= 72 && squareSize >= 33) {
\r
2587 /* A & C are available in most sizes now */
\r
2588 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2589 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2590 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2591 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2592 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2595 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2598 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2601 } else { // Smirf-like
\r
2602 if(gameInfo.variant == VariantSChess) {
\r
2603 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2607 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2608 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2609 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2612 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2613 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2614 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2615 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2616 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2617 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2620 } else { // WinBoard standard
\r
2621 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2622 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2623 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2628 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2629 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2630 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2631 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2632 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2633 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2634 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2635 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2636 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2637 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2638 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2639 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2640 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2641 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2642 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2643 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2653 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2654 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2655 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2656 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2657 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2658 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2660 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2661 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2662 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2663 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2664 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2665 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2666 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2667 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2668 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2669 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2670 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2671 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2672 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2674 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2675 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2676 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2677 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2678 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2679 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2680 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2681 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2682 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2683 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2684 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2685 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2688 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2689 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2690 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2691 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2692 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2693 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2694 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2695 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2696 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2697 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2698 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2699 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2700 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2701 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2702 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2706 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2707 /* special Shogi support in this size */
\r
2708 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2709 for (piece = WhitePawn;
\r
2710 (int) piece < (int) BlackPawn;
\r
2711 piece = (ChessSquare) ((int) piece + 1)) {
\r
2712 if (pieceBitmap[i][piece] != NULL)
\r
2713 DeleteObject(pieceBitmap[i][piece]);
\r
2716 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2717 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2718 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2719 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2720 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2721 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2722 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2723 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2724 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2725 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2726 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2727 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2728 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2729 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2730 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2731 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2732 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2733 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2734 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2735 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2736 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2737 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2738 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2739 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2740 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2741 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2742 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2743 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2744 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2745 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2746 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2747 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2748 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2749 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2750 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2751 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2752 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2753 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2754 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2755 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2756 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2757 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2763 PieceBitmap(ChessSquare p, int kind)
\r
2765 if ((int) p >= (int) BlackPawn)
\r
2766 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2768 return pieceBitmap[kind][(int) p];
\r
2771 /***************************************************************/
\r
2773 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2774 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2776 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2777 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2781 SquareToPos(int row, int column, int * x, int * y)
\r
2784 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2785 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2787 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2788 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2793 DrawCoordsOnDC(HDC hdc)
\r
2795 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2796 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2797 char str[2] = { NULLCHAR, NULLCHAR };
\r
2798 int oldMode, oldAlign, x, y, start, i;
\r
2802 if (!appData.showCoords)
\r
2805 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2807 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2808 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2809 oldAlign = GetTextAlign(hdc);
\r
2810 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2812 y = boardRect.top + lineGap;
\r
2813 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2816 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2817 x += border - lineGap - 4; y += squareSize - 6;
\r
2819 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2820 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2821 str[0] = files[start + i];
\r
2822 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2823 y += squareSize + lineGap;
\r
2826 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2829 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2830 x += -border + 4; y += border - squareSize + 6;
\r
2832 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2833 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2834 str[0] = ranks[start + i];
\r
2835 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2836 x += squareSize + lineGap;
\r
2839 SelectObject(hdc, oldBrush);
\r
2840 SetBkMode(hdc, oldMode);
\r
2841 SetTextAlign(hdc, oldAlign);
\r
2842 SelectObject(hdc, oldFont);
\r
2846 DrawGridOnDC(HDC hdc)
\r
2850 if (lineGap != 0) {
\r
2851 oldPen = SelectObject(hdc, gridPen);
\r
2852 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2853 SelectObject(hdc, oldPen);
\r
2857 #define HIGHLIGHT_PEN 0
\r
2858 #define PREMOVE_PEN 1
\r
2861 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2864 HPEN oldPen, hPen;
\r
2865 if (lineGap == 0) return;
\r
2867 x1 = boardRect.left +
\r
2868 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2869 y1 = boardRect.top +
\r
2870 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2872 x1 = boardRect.left +
\r
2873 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2874 y1 = boardRect.top +
\r
2875 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2877 hPen = pen ? premovePen : highlightPen;
\r
2878 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2879 MoveToEx(hdc, x1, y1, NULL);
\r
2880 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2881 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2882 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2883 LineTo(hdc, x1, y1);
\r
2884 SelectObject(hdc, oldPen);
\r
2888 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2891 for (i=0; i<2; i++) {
\r
2892 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2893 DrawHighlightOnDC(hdc, TRUE,
\r
2894 h->sq[i].x, h->sq[i].y,
\r
2899 /* Note: sqcolor is used only in monoMode */
\r
2900 /* Note that this code is largely duplicated in woptions.c,
\r
2901 function DrawSampleSquare, so that needs to be updated too */
\r
2903 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2905 HBITMAP oldBitmap;
\r
2909 if (appData.blindfold) return;
\r
2911 /* [AS] Use font-based pieces if needed */
\r
2912 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2913 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2914 CreatePiecesFromFont();
\r
2916 if( fontBitmapSquareSize == squareSize ) {
\r
2917 int index = TranslatePieceToFontPiece(piece);
\r
2919 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2921 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2922 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2926 squareSize, squareSize,
\r
2931 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2933 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2934 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2938 squareSize, squareSize,
\r
2947 if (appData.monoMode) {
\r
2948 SelectObject(tmphdc, PieceBitmap(piece,
\r
2949 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2950 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2951 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2953 HBRUSH xBrush = whitePieceBrush;
\r
2954 tmpSize = squareSize;
\r
2955 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2957 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2958 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2959 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2960 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2961 x += (squareSize - minorSize)>>1;
\r
2962 y += squareSize - minorSize - 2;
\r
2963 tmpSize = minorSize;
\r
2965 if (color || appData.allWhite ) {
\r
2966 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2968 oldBrush = SelectObject(hdc, xBrush);
\r
2969 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2970 if(appData.upsideDown && color==flipView)
\r
2971 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2973 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2974 /* Use black for outline of white pieces */
\r
2975 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2976 if(appData.upsideDown && color==flipView)
\r
2977 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2979 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2980 } else if(appData.pieceDirectory[0]) {
\r
2981 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2982 oldBrush = SelectObject(hdc, xBrush);
\r
2983 if(appData.upsideDown && color==flipView)
\r
2984 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2986 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2987 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2988 if(appData.upsideDown && color==flipView)
\r
2989 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2991 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2993 /* Use square color for details of black pieces */
\r
2994 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2995 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2996 if(appData.upsideDown && !flipView)
\r
2997 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2999 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3001 SelectObject(hdc, oldBrush);
\r
3002 SelectObject(tmphdc, oldBitmap);
\r
3006 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3007 int GetBackTextureMode( int algo )
\r
3009 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3013 case BACK_TEXTURE_MODE_PLAIN:
\r
3014 result = 1; /* Always use identity map */
\r
3016 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3017 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3025 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3026 to handle redraws cleanly (as random numbers would always be different).
\r
3028 VOID RebuildTextureSquareInfo()
\r
3038 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3040 if( liteBackTexture != NULL ) {
\r
3041 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3042 lite_w = bi.bmWidth;
\r
3043 lite_h = bi.bmHeight;
\r
3047 if( darkBackTexture != NULL ) {
\r
3048 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3049 dark_w = bi.bmWidth;
\r
3050 dark_h = bi.bmHeight;
\r
3054 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3055 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3056 if( (col + row) & 1 ) {
\r
3058 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3059 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3060 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3062 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3063 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3064 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3066 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3067 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3072 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3073 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3074 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3076 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3077 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3078 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3080 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3081 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3088 /* [AS] Arrow highlighting support */
\r
3090 static double A_WIDTH = 5; /* Width of arrow body */
\r
3092 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3093 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3095 static double Sqr( double x )
\r
3100 static int Round( double x )
\r
3102 return (int) (x + 0.5);
\r
3105 /* Draw an arrow between two points using current settings */
\r
3106 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3109 double dx, dy, j, k, x, y;
\r
3111 if( d_x == s_x ) {
\r
3112 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3114 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3117 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3118 arrow[1].y = d_y - h;
\r
3120 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3121 arrow[2].y = d_y - h;
\r
3126 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3127 arrow[5].y = d_y - h;
\r
3129 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3130 arrow[4].y = d_y - h;
\r
3132 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3135 else if( d_y == s_y ) {
\r
3136 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3139 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3141 arrow[1].x = d_x - w;
\r
3142 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3144 arrow[2].x = d_x - w;
\r
3145 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3150 arrow[5].x = d_x - w;
\r
3151 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3153 arrow[4].x = d_x - w;
\r
3154 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3157 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3160 /* [AS] Needed a lot of paper for this! :-) */
\r
3161 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3162 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3164 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3166 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3171 arrow[0].x = Round(x - j);
\r
3172 arrow[0].y = Round(y + j*dx);
\r
3174 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3175 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3178 x = (double) d_x - k;
\r
3179 y = (double) d_y - k*dy;
\r
3182 x = (double) d_x + k;
\r
3183 y = (double) d_y + k*dy;
\r
3186 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3188 arrow[6].x = Round(x - j);
\r
3189 arrow[6].y = Round(y + j*dx);
\r
3191 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3192 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3194 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3195 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3200 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3201 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3204 Polygon( hdc, arrow, 7 );
\r
3207 /* [AS] Draw an arrow between two squares */
\r
3208 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3210 int s_x, s_y, d_x, d_y;
\r
3217 if( s_col == d_col && s_row == d_row ) {
\r
3221 /* Get source and destination points */
\r
3222 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3223 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3226 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3228 else if( d_y < s_y ) {
\r
3229 d_y += squareSize / 2 + squareSize / 4;
\r
3232 d_y += squareSize / 2;
\r
3236 d_x += squareSize / 2 - squareSize / 4;
\r
3238 else if( d_x < s_x ) {
\r
3239 d_x += squareSize / 2 + squareSize / 4;
\r
3242 d_x += squareSize / 2;
\r
3245 s_x += squareSize / 2;
\r
3246 s_y += squareSize / 2;
\r
3248 /* Adjust width */
\r
3249 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3252 stLB.lbStyle = BS_SOLID;
\r
3253 stLB.lbColor = appData.highlightArrowColor;
\r
3256 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3257 holdpen = SelectObject( hdc, hpen );
\r
3258 hbrush = CreateBrushIndirect( &stLB );
\r
3259 holdbrush = SelectObject( hdc, hbrush );
\r
3261 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3263 SelectObject( hdc, holdpen );
\r
3264 SelectObject( hdc, holdbrush );
\r
3265 DeleteObject( hpen );
\r
3266 DeleteObject( hbrush );
\r
3269 BOOL HasHighlightInfo()
\r
3271 BOOL result = FALSE;
\r
3273 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3274 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3282 BOOL IsDrawArrowEnabled()
\r
3284 BOOL result = FALSE;
\r
3286 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3293 VOID DrawArrowHighlight( HDC hdc )
\r
3295 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3296 DrawArrowBetweenSquares( hdc,
\r
3297 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3298 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3302 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3304 HRGN result = NULL;
\r
3306 if( HasHighlightInfo() ) {
\r
3307 int x1, y1, x2, y2;
\r
3308 int sx, sy, dx, dy;
\r
3310 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3311 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3313 sx = MIN( x1, x2 );
\r
3314 sy = MIN( y1, y2 );
\r
3315 dx = MAX( x1, x2 ) + squareSize;
\r
3316 dy = MAX( y1, y2 ) + squareSize;
\r
3318 result = CreateRectRgn( sx, sy, dx, dy );
\r
3325 Warning: this function modifies the behavior of several other functions.
\r
3327 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3328 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3329 repaint is scattered all over the place, which is not good for features such as
\r
3330 "arrow highlighting" that require a full repaint of the board.
\r
3332 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3333 user interaction, when speed is not so important) but especially to avoid errors
\r
3334 in the displayed graphics.
\r
3336 In such patched places, I always try refer to this function so there is a single
\r
3337 place to maintain knowledge.
\r
3339 To restore the original behavior, just return FALSE unconditionally.
\r
3341 BOOL IsFullRepaintPreferrable()
\r
3343 BOOL result = FALSE;
\r
3345 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3346 /* Arrow may appear on the board */
\r
3354 This function is called by DrawPosition to know whether a full repaint must
\r
3357 Only DrawPosition may directly call this function, which makes use of
\r
3358 some state information. Other function should call DrawPosition specifying
\r
3359 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3361 BOOL DrawPositionNeedsFullRepaint()
\r
3363 BOOL result = FALSE;
\r
3366 Probably a slightly better policy would be to trigger a full repaint
\r
3367 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3368 but animation is fast enough that it's difficult to notice.
\r
3370 if( animInfo.piece == EmptySquare ) {
\r
3371 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3379 static HBITMAP borderBitmap;
\r
3382 DrawBackgroundOnDC(HDC hdc)
\r
3388 static char oldBorder[MSG_SIZ];
\r
3389 int w = 600, h = 600, mode;
\r
3391 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3392 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3393 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3395 if(borderBitmap == NULL) { // loading failed, use white
\r
3396 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3399 tmphdc = CreateCompatibleDC(hdc);
\r
3400 hbm = SelectObject(tmphdc, borderBitmap);
\r
3401 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3405 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3406 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3407 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3408 SetStretchBltMode(hdc, mode);
\r
3409 SelectObject(tmphdc, hbm);
\r
3414 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3416 int row, column, x, y, square_color, piece_color;
\r
3417 ChessSquare piece;
\r
3419 HDC texture_hdc = NULL;
\r
3421 /* [AS] Initialize background textures if needed */
\r
3422 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3423 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3424 if( backTextureSquareSize != squareSize
\r
3425 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3426 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3427 backTextureSquareSize = squareSize;
\r
3428 RebuildTextureSquareInfo();
\r
3431 texture_hdc = CreateCompatibleDC( hdc );
\r
3434 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3435 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3437 SquareToPos(row, column, &x, &y);
\r
3439 piece = board[row][column];
\r
3441 square_color = ((column + row) % 2) == 1;
\r
3442 if( gameInfo.variant == VariantXiangqi ) {
\r
3443 square_color = !InPalace(row, column);
\r
3444 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3445 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3447 piece_color = (int) piece < (int) BlackPawn;
\r
3450 /* [HGM] holdings file: light square or black */
\r
3451 if(column == BOARD_LEFT-2) {
\r
3452 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3455 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3459 if(column == BOARD_RGHT + 1 ) {
\r
3460 if( row < gameInfo.holdingsSize )
\r
3463 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3467 if(column == BOARD_LEFT-1 ) /* left align */
\r
3468 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3469 else if( column == BOARD_RGHT) /* right align */
\r
3470 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3472 if (appData.monoMode) {
\r
3473 if (piece == EmptySquare) {
\r
3474 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3475 square_color ? WHITENESS : BLACKNESS);
\r
3477 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3480 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3481 /* [AS] Draw the square using a texture bitmap */
\r
3482 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3483 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3484 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3487 squareSize, squareSize,
\r
3490 backTextureSquareInfo[r][c].mode,
\r
3491 backTextureSquareInfo[r][c].x,
\r
3492 backTextureSquareInfo[r][c].y );
\r
3494 SelectObject( texture_hdc, hbm );
\r
3496 if (piece != EmptySquare) {
\r
3497 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3501 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3503 oldBrush = SelectObject(hdc, brush );
\r
3504 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3505 SelectObject(hdc, oldBrush);
\r
3506 if (piece != EmptySquare)
\r
3507 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3512 if( texture_hdc != NULL ) {
\r
3513 DeleteDC( texture_hdc );
\r
3517 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3518 void fputDW(FILE *f, int x)
\r
3520 fputc(x & 255, f);
\r
3521 fputc(x>>8 & 255, f);
\r
3522 fputc(x>>16 & 255, f);
\r
3523 fputc(x>>24 & 255, f);
\r
3526 #define MAX_CLIPS 200 /* more than enough */
\r
3529 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3531 // HBITMAP bufferBitmap;
\r
3536 int w = 100, h = 50;
\r
3538 if(logo == NULL) {
\r
3539 if(!logoHeight) return;
\r
3540 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3542 // GetClientRect(hwndMain, &Rect);
\r
3543 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3544 // Rect.bottom-Rect.top+1);
\r
3545 tmphdc = CreateCompatibleDC(hdc);
\r
3546 hbm = SelectObject(tmphdc, logo);
\r
3547 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3551 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3552 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3553 SelectObject(tmphdc, hbm);
\r
3561 HDC hdc = GetDC(hwndMain);
\r
3562 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3563 if(appData.autoLogo) {
\r
3565 switch(gameMode) { // pick logos based on game mode
\r
3566 case IcsObserving:
\r
3567 whiteLogo = second.programLogo; // ICS logo
\r
3568 blackLogo = second.programLogo;
\r
3571 case IcsPlayingWhite:
\r
3572 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3573 blackLogo = second.programLogo; // ICS logo
\r
3575 case IcsPlayingBlack:
\r
3576 whiteLogo = second.programLogo; // ICS logo
\r
3577 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3579 case TwoMachinesPlay:
\r
3580 if(first.twoMachinesColor[0] == 'b') {
\r
3581 whiteLogo = second.programLogo;
\r
3582 blackLogo = first.programLogo;
\r
3585 case MachinePlaysWhite:
\r
3586 blackLogo = userLogo;
\r
3588 case MachinePlaysBlack:
\r
3589 whiteLogo = userLogo;
\r
3590 blackLogo = first.programLogo;
\r
3593 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3594 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3595 ReleaseDC(hwndMain, hdc);
\r
3600 UpdateLogos(int display)
\r
3601 { // called after loading new engine(s), in tourney or from menu
\r
3602 LoadLogo(&first, 0, FALSE);
\r
3603 LoadLogo(&second, 1, appData.icsActive);
\r
3604 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3605 if(display) DisplayLogos();
\r
3608 static HDC hdcSeek;
\r
3610 // [HGM] seekgraph
\r
3611 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3614 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3615 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3616 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3617 SelectObject( hdcSeek, hp );
\r
3620 // front-end wrapper for drawing functions to do rectangles
\r
3621 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3626 if (hdcSeek == NULL) {
\r
3627 hdcSeek = GetDC(hwndMain);
\r
3628 if (!appData.monoMode) {
\r
3629 SelectPalette(hdcSeek, hPal, FALSE);
\r
3630 RealizePalette(hdcSeek);
\r
3633 hp = SelectObject( hdcSeek, gridPen );
\r
3634 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3635 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3636 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3637 SelectObject( hdcSeek, hp );
\r
3640 // front-end wrapper for putting text in graph
\r
3641 void DrawSeekText(char *buf, int x, int y)
\r
3644 SetBkMode( hdcSeek, TRANSPARENT );
\r
3645 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3646 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3649 void DrawSeekDot(int x, int y, int color)
\r
3651 int square = color & 0x80;
\r
3652 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3653 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3656 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3657 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3659 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3660 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3661 SelectObject(hdcSeek, oldBrush);
\r
3664 void DrawSeekOpen()
\r
3668 void DrawSeekClose()
\r
3673 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3675 static Board lastReq[2], lastDrawn[2];
\r
3676 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3677 static int lastDrawnFlipView = 0;
\r
3678 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3679 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3682 HBITMAP bufferBitmap;
\r
3683 HBITMAP oldBitmap;
\r
3685 HRGN clips[MAX_CLIPS];
\r
3686 ChessSquare dragged_piece = EmptySquare;
\r
3687 int nr = twoBoards*partnerUp;
\r
3689 /* I'm undecided on this - this function figures out whether a full
\r
3690 * repaint is necessary on its own, so there's no real reason to have the
\r
3691 * caller tell it that. I think this can safely be set to FALSE - but
\r
3692 * if we trust the callers not to request full repaints unnessesarily, then
\r
3693 * we could skip some clipping work. In other words, only request a full
\r
3694 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3695 * gamestart and similar) --Hawk
\r
3697 Boolean fullrepaint = repaint;
\r
3699 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3701 if( DrawPositionNeedsFullRepaint() ) {
\r
3702 fullrepaint = TRUE;
\r
3705 if (board == NULL) {
\r
3706 if (!lastReqValid[nr]) {
\r
3709 board = lastReq[nr];
\r
3711 CopyBoard(lastReq[nr], board);
\r
3712 lastReqValid[nr] = 1;
\r
3715 if (doingSizing) {
\r
3719 if (IsIconic(hwndMain)) {
\r
3723 if (hdc == NULL) {
\r
3724 hdc = GetDC(hwndMain);
\r
3725 if (!appData.monoMode) {
\r
3726 SelectPalette(hdc, hPal, FALSE);
\r
3727 RealizePalette(hdc);
\r
3731 releaseDC = FALSE;
\r
3734 /* Create some work-DCs */
\r
3735 hdcmem = CreateCompatibleDC(hdc);
\r
3736 tmphdc = CreateCompatibleDC(hdc);
\r
3738 /* If dragging is in progress, we temporarely remove the piece */
\r
3739 /* [HGM] or temporarily decrease count if stacked */
\r
3740 /* !! Moved to before board compare !! */
\r
3741 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3742 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3743 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3744 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3745 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3747 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3748 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3749 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3751 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3754 /* Figure out which squares need updating by comparing the
\r
3755 * newest board with the last drawn board and checking if
\r
3756 * flipping has changed.
\r
3758 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3759 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3760 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3761 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3762 SquareToPos(row, column, &x, &y);
\r
3763 clips[num_clips++] =
\r
3764 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3768 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3769 for (i=0; i<2; i++) {
\r
3770 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3771 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3772 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3773 lastDrawnHighlight.sq[i].y >= 0) {
\r
3774 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3775 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3776 clips[num_clips++] =
\r
3777 CreateRectRgn(x - lineGap, y - lineGap,
\r
3778 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3780 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3781 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3782 clips[num_clips++] =
\r
3783 CreateRectRgn(x - lineGap, y - lineGap,
\r
3784 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3788 for (i=0; i<2; i++) {
\r
3789 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3790 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3791 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3792 lastDrawnPremove.sq[i].y >= 0) {
\r
3793 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3794 lastDrawnPremove.sq[i].x, &x, &y);
\r
3795 clips[num_clips++] =
\r
3796 CreateRectRgn(x - lineGap, y - lineGap,
\r
3797 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3799 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3800 premoveHighlightInfo.sq[i].y >= 0) {
\r
3801 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3802 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3803 clips[num_clips++] =
\r
3804 CreateRectRgn(x - lineGap, y - lineGap,
\r
3805 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3809 } else { // nr == 1
\r
3810 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3811 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3812 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3813 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3814 for (i=0; i<2; i++) {
\r
3815 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3816 partnerHighlightInfo.sq[i].y >= 0) {
\r
3817 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3818 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3819 clips[num_clips++] =
\r
3820 CreateRectRgn(x - lineGap, y - lineGap,
\r
3821 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3823 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3824 oldPartnerHighlight.sq[i].y >= 0) {
\r
3825 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3826 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3827 clips[num_clips++] =
\r
3828 CreateRectRgn(x - lineGap, y - lineGap,
\r
3829 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3834 fullrepaint = TRUE;
\r
3837 /* Create a buffer bitmap - this is the actual bitmap
\r
3838 * being written to. When all the work is done, we can
\r
3839 * copy it to the real DC (the screen). This avoids
\r
3840 * the problems with flickering.
\r
3842 GetClientRect(hwndMain, &Rect);
\r
3843 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3844 Rect.bottom-Rect.top+1);
\r
3845 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3846 if (!appData.monoMode) {
\r
3847 SelectPalette(hdcmem, hPal, FALSE);
\r
3850 /* Create clips for dragging */
\r
3851 if (!fullrepaint) {
\r
3852 if (dragInfo.from.x >= 0) {
\r
3853 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3854 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3856 if (dragInfo.start.x >= 0) {
\r
3857 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3858 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3860 if (dragInfo.pos.x >= 0) {
\r
3861 x = dragInfo.pos.x - squareSize / 2;
\r
3862 y = dragInfo.pos.y - squareSize / 2;
\r
3863 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3865 if (dragInfo.lastpos.x >= 0) {
\r
3866 x = dragInfo.lastpos.x - squareSize / 2;
\r
3867 y = dragInfo.lastpos.y - squareSize / 2;
\r
3868 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3872 /* Are we animating a move?
\r
3874 * - remove the piece from the board (temporarely)
\r
3875 * - calculate the clipping region
\r
3877 if (!fullrepaint) {
\r
3878 if (animInfo.piece != EmptySquare) {
\r
3879 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3880 x = boardRect.left + animInfo.lastpos.x;
\r
3881 y = boardRect.top + animInfo.lastpos.y;
\r
3882 x2 = boardRect.left + animInfo.pos.x;
\r
3883 y2 = boardRect.top + animInfo.pos.y;
\r
3884 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3885 /* Slight kludge. The real problem is that after AnimateMove is
\r
3886 done, the position on the screen does not match lastDrawn.
\r
3887 This currently causes trouble only on e.p. captures in
\r
3888 atomic, where the piece moves to an empty square and then
\r
3889 explodes. The old and new positions both had an empty square
\r
3890 at the destination, but animation has drawn a piece there and
\r
3891 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3892 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3896 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3897 if (num_clips == 0)
\r
3898 fullrepaint = TRUE;
\r
3900 /* Set clipping on the memory DC */
\r
3901 if (!fullrepaint) {
\r
3902 SelectClipRgn(hdcmem, clips[0]);
\r
3903 for (x = 1; x < num_clips; x++) {
\r
3904 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3905 abort(); // this should never ever happen!
\r
3909 /* Do all the drawing to the memory DC */
\r
3910 if(explodeInfo.radius) { // [HGM] atomic
\r
3912 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3913 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3914 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3915 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3916 x += squareSize/2;
\r
3917 y += squareSize/2;
\r
3918 if(!fullrepaint) {
\r
3919 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3920 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3922 DrawGridOnDC(hdcmem);
\r
3923 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3924 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3925 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3926 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3927 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3928 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3929 SelectObject(hdcmem, oldBrush);
\r
3931 if(border) DrawBackgroundOnDC(hdcmem);
\r
3932 DrawGridOnDC(hdcmem);
\r
3933 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3934 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3935 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3937 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3938 oldPartnerHighlight = partnerHighlightInfo;
\r
3940 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3942 if(nr == 0) // [HGM] dual: markers only on left board
\r
3943 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3944 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3945 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3946 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3947 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3948 SquareToPos(row, column, &x, &y);
\r
3949 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3950 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3951 SelectObject(hdcmem, oldBrush);
\r
3956 if( appData.highlightMoveWithArrow ) {
\r
3957 DrawArrowHighlight(hdcmem);
\r
3960 DrawCoordsOnDC(hdcmem);
\r
3962 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3963 /* to make sure lastDrawn contains what is actually drawn */
\r
3965 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3966 if (dragged_piece != EmptySquare) {
\r
3967 /* [HGM] or restack */
\r
3968 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3969 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3971 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3972 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3973 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3974 x = dragInfo.pos.x - squareSize / 2;
\r
3975 y = dragInfo.pos.y - squareSize / 2;
\r
3976 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3977 ((int) dragInfo.piece < (int) BlackPawn),
\r
3978 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3981 /* Put the animated piece back into place and draw it */
\r
3982 if (animInfo.piece != EmptySquare) {
\r
3983 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3984 x = boardRect.left + animInfo.pos.x;
\r
3985 y = boardRect.top + animInfo.pos.y;
\r
3986 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3987 ((int) animInfo.piece < (int) BlackPawn),
\r
3988 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3991 /* Release the bufferBitmap by selecting in the old bitmap
\r
3992 * and delete the memory DC
\r
3994 SelectObject(hdcmem, oldBitmap);
\r
3997 /* Set clipping on the target DC */
\r
3998 if (!fullrepaint) {
\r
3999 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4001 GetRgnBox(clips[x], &rect);
\r
4002 DeleteObject(clips[x]);
\r
4003 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4004 rect.right + wpMain.width/2, rect.bottom);
\r
4006 SelectClipRgn(hdc, clips[0]);
\r
4007 for (x = 1; x < num_clips; x++) {
\r
4008 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4009 abort(); // this should never ever happen!
\r
4013 /* Copy the new bitmap onto the screen in one go.
\r
4014 * This way we avoid any flickering
\r
4016 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4017 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4018 boardRect.right - boardRect.left,
\r
4019 boardRect.bottom - boardRect.top,
\r
4020 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4021 if(saveDiagFlag) {
\r
4022 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4023 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4025 GetObject(bufferBitmap, sizeof(b), &b);
\r
4026 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4027 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4028 bih.biWidth = b.bmWidth;
\r
4029 bih.biHeight = b.bmHeight;
\r
4031 bih.biBitCount = b.bmBitsPixel;
\r
4032 bih.biCompression = 0;
\r
4033 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4034 bih.biXPelsPerMeter = 0;
\r
4035 bih.biYPelsPerMeter = 0;
\r
4036 bih.biClrUsed = 0;
\r
4037 bih.biClrImportant = 0;
\r
4038 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4039 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4040 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4041 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4043 wb = b.bmWidthBytes;
\r
4045 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4046 int k = ((int*) pData)[i];
\r
4047 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4048 if(j >= 16) break;
\r
4050 if(j >= nrColors) nrColors = j+1;
\r
4052 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4054 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4055 for(w=0; w<(wb>>2); w+=2) {
\r
4056 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4057 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4058 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4059 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4060 pData[p++] = m | j<<4;
\r
4062 while(p&3) pData[p++] = 0;
\r
4065 wb = ((wb+31)>>5)<<2;
\r
4067 // write BITMAPFILEHEADER
\r
4068 fprintf(diagFile, "BM");
\r
4069 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4070 fputDW(diagFile, 0);
\r
4071 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4072 // write BITMAPINFOHEADER
\r
4073 fputDW(diagFile, 40);
\r
4074 fputDW(diagFile, b.bmWidth);
\r
4075 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4076 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4077 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4078 fputDW(diagFile, 0);
\r
4079 fputDW(diagFile, 0);
\r
4080 fputDW(diagFile, 0);
\r
4081 fputDW(diagFile, 0);
\r
4082 fputDW(diagFile, 0);
\r
4083 fputDW(diagFile, 0);
\r
4084 // write color table
\r
4086 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4087 // write bitmap data
\r
4088 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4089 fputc(pData[i], diagFile);
\r
4094 SelectObject(tmphdc, oldBitmap);
\r
4096 /* Massive cleanup */
\r
4097 for (x = 0; x < num_clips; x++)
\r
4098 DeleteObject(clips[x]);
\r
4101 DeleteObject(bufferBitmap);
\r
4104 ReleaseDC(hwndMain, hdc);
\r
4106 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4108 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4110 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4113 /* CopyBoard(lastDrawn, board);*/
\r
4114 lastDrawnHighlight = highlightInfo;
\r
4115 lastDrawnPremove = premoveHighlightInfo;
\r
4116 lastDrawnFlipView = flipView;
\r
4117 lastDrawnValid[nr] = 1;
\r
4120 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4125 saveDiagFlag = 1; diagFile = f;
\r
4126 HDCDrawPosition(NULL, TRUE, NULL);
\r
4134 /*---------------------------------------------------------------------------*\
\r
4135 | CLIENT PAINT PROCEDURE
\r
4136 | This is the main event-handler for the WM_PAINT message.
\r
4138 \*---------------------------------------------------------------------------*/
\r
4140 PaintProc(HWND hwnd)
\r
4146 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4147 if (IsIconic(hwnd)) {
\r
4148 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4150 if (!appData.monoMode) {
\r
4151 SelectPalette(hdc, hPal, FALSE);
\r
4152 RealizePalette(hdc);
\r
4154 HDCDrawPosition(hdc, 1, NULL);
\r
4155 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4156 flipView = !flipView; partnerUp = !partnerUp;
\r
4157 HDCDrawPosition(hdc, 1, NULL);
\r
4158 flipView = !flipView; partnerUp = !partnerUp;
\r
4161 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4162 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4163 ETO_CLIPPED|ETO_OPAQUE,
\r
4164 &messageRect, messageText, strlen(messageText), NULL);
\r
4165 SelectObject(hdc, oldFont);
\r
4166 DisplayBothClocks();
\r
4169 EndPaint(hwnd,&ps);
\r
4177 * If the user selects on a border boundary, return -1; if off the board,
\r
4178 * return -2. Otherwise map the event coordinate to the square.
\r
4179 * The offset boardRect.left or boardRect.top must already have been
\r
4180 * subtracted from x.
\r
4182 int EventToSquare(x, limit)
\r
4187 if (x < lineGap + border)
\r
4189 x -= lineGap + border;
\r
4190 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4192 x /= (squareSize + lineGap);
\r
4204 DropEnable dropEnables[] = {
\r
4205 { 'P', DP_Pawn, N_("Pawn") },
\r
4206 { 'N', DP_Knight, N_("Knight") },
\r
4207 { 'B', DP_Bishop, N_("Bishop") },
\r
4208 { 'R', DP_Rook, N_("Rook") },
\r
4209 { 'Q', DP_Queen, N_("Queen") },
\r
4213 SetupDropMenu(HMENU hmenu)
\r
4215 int i, count, enable;
\r
4217 extern char white_holding[], black_holding[];
\r
4218 char item[MSG_SIZ];
\r
4220 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4221 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4222 dropEnables[i].piece);
\r
4224 while (p && *p++ == dropEnables[i].piece) count++;
\r
4225 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4226 enable = count > 0 || !appData.testLegality
\r
4227 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4228 && !appData.icsActive);
\r
4229 ModifyMenu(hmenu, dropEnables[i].command,
\r
4230 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4231 dropEnables[i].command, item);
\r
4235 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4237 dragInfo.lastpos.x = boardRect.left + x;
\r
4238 dragInfo.lastpos.y = boardRect.top + y;
\r
4239 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4240 dragInfo.from.x = fromX;
\r
4241 dragInfo.from.y = fromY;
\r
4242 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4243 dragInfo.start = dragInfo.from;
\r
4244 SetCapture(hwndMain);
\r
4247 void DragPieceEnd(int x, int y)
\r
4250 dragInfo.start.x = dragInfo.start.y = -1;
\r
4251 dragInfo.from = dragInfo.start;
\r
4252 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4255 void ChangeDragPiece(ChessSquare piece)
\r
4257 dragInfo.piece = piece;
\r
4260 /* Event handler for mouse messages */
\r
4262 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4266 static int recursive = 0;
\r
4268 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4271 if (message == WM_MBUTTONUP) {
\r
4272 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4273 to the middle button: we simulate pressing the left button too!
\r
4275 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4276 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4282 pt.x = LOWORD(lParam);
\r
4283 pt.y = HIWORD(lParam);
\r
4284 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4285 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4286 if (!flipView && y >= 0) {
\r
4287 y = BOARD_HEIGHT - 1 - y;
\r
4289 if (flipView && x >= 0) {
\r
4290 x = BOARD_WIDTH - 1 - x;
\r
4293 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4294 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4296 switch (message) {
\r
4297 case WM_LBUTTONDOWN:
\r
4298 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4299 ClockClick(flipClock); break;
\r
4300 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4301 ClockClick(!flipClock); break;
\r
4303 dragInfo.start.x = dragInfo.start.y = -1;
\r
4304 dragInfo.from = dragInfo.start;
\r
4305 if(fromX == -1 && frozen) { // not sure where this is for
\r
4306 fromX = fromY = -1;
\r
4307 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4310 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4311 DrawPosition(TRUE, NULL);
\r
4314 case WM_LBUTTONUP:
\r
4315 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4316 DrawPosition(TRUE, NULL);
\r
4319 case WM_MOUSEMOVE:
\r
4320 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4321 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4322 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4323 if ((appData.animateDragging || appData.highlightDragging)
\r
4324 && (wParam & MK_LBUTTON)
\r
4325 && dragInfo.from.x >= 0)
\r
4327 BOOL full_repaint = FALSE;
\r
4329 if (appData.animateDragging) {
\r
4330 dragInfo.pos = pt;
\r
4332 if (appData.highlightDragging) {
\r
4333 SetHighlights(fromX, fromY, x, y);
\r
4334 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4335 full_repaint = TRUE;
\r
4339 DrawPosition( full_repaint, NULL);
\r
4341 dragInfo.lastpos = dragInfo.pos;
\r
4345 case WM_MOUSEWHEEL: // [DM]
\r
4346 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4347 /* Mouse Wheel is being rolled forward
\r
4348 * Play moves forward
\r
4350 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4351 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4352 /* Mouse Wheel is being rolled backward
\r
4353 * Play moves backward
\r
4355 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4356 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4360 case WM_MBUTTONUP:
\r
4361 case WM_RBUTTONUP:
\r
4363 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4366 case WM_MBUTTONDOWN:
\r
4367 case WM_RBUTTONDOWN:
\r
4370 fromX = fromY = -1;
\r
4371 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4372 dragInfo.start.x = dragInfo.start.y = -1;
\r
4373 dragInfo.from = dragInfo.start;
\r
4374 dragInfo.lastpos = dragInfo.pos;
\r
4375 if (appData.highlightDragging) {
\r
4376 ClearHighlights();
\r
4379 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4380 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4381 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4382 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4383 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4387 DrawPosition(TRUE, NULL);
\r
4389 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4392 if (message == WM_MBUTTONDOWN) {
\r
4393 buttonCount = 3; /* even if system didn't think so */
\r
4394 if (wParam & MK_SHIFT)
\r
4395 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4397 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4398 } else { /* message == WM_RBUTTONDOWN */
\r
4399 /* Just have one menu, on the right button. Windows users don't
\r
4400 think to try the middle one, and sometimes other software steals
\r
4401 it, or it doesn't really exist. */
\r
4402 if(gameInfo.variant != VariantShogi)
\r
4403 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4405 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4409 SetCapture(hwndMain);
\r
4412 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4413 SetupDropMenu(hmenu);
\r
4414 MenuPopup(hwnd, pt, hmenu, -1);
\r
4424 /* Preprocess messages for buttons in main window */
\r
4426 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4428 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4431 for (i=0; i<N_BUTTONS; i++) {
\r
4432 if (buttonDesc[i].id == id) break;
\r
4434 if (i == N_BUTTONS) return 0;
\r
4435 switch (message) {
\r
4440 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4441 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4448 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4451 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4452 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4453 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4454 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4456 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4458 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4459 TypeInEvent((char)wParam);
\r
4465 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4468 /* Process messages for Promotion dialog box */
\r
4470 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4474 switch (message) {
\r
4475 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4476 /* Center the dialog over the application window */
\r
4477 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4478 Translate(hDlg, DLG_PromotionKing);
\r
4479 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4480 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4481 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4482 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4483 SW_SHOW : SW_HIDE);
\r
4484 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4485 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4486 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4487 PieceToChar(WhiteAngel) != '~') ||
\r
4488 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4489 PieceToChar(BlackAngel) != '~') ) ?
\r
4490 SW_SHOW : SW_HIDE);
\r
4491 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4492 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4493 PieceToChar(WhiteMarshall) != '~') ||
\r
4494 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4495 PieceToChar(BlackMarshall) != '~') ) ?
\r
4496 SW_SHOW : SW_HIDE);
\r
4497 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4498 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4499 gameInfo.variant != VariantShogi ?
\r
4500 SW_SHOW : SW_HIDE);
\r
4501 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4502 gameInfo.variant != VariantShogi ?
\r
4503 SW_SHOW : SW_HIDE);
\r
4504 if(gameInfo.variant == VariantShogi) {
\r
4505 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4506 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4507 SetWindowText(hDlg, "Promote?");
\r
4509 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4510 gameInfo.variant == VariantSuper ?
\r
4511 SW_SHOW : SW_HIDE);
\r
4514 case WM_COMMAND: /* message: received a command */
\r
4515 switch (LOWORD(wParam)) {
\r
4517 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4518 ClearHighlights();
\r
4519 DrawPosition(FALSE, NULL);
\r
4522 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4525 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4528 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4529 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4532 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4533 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4535 case PB_Chancellor:
\r
4536 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4538 case PB_Archbishop:
\r
4539 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4542 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4547 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4548 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4549 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4550 fromX = fromY = -1;
\r
4551 if (!appData.highlightLastMove) {
\r
4552 ClearHighlights();
\r
4553 DrawPosition(FALSE, NULL);
\r
4560 /* Pop up promotion dialog */
\r
4562 PromotionPopup(HWND hwnd)
\r
4566 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4567 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4568 hwnd, (DLGPROC)lpProc);
\r
4569 FreeProcInstance(lpProc);
\r
4575 DrawPosition(TRUE, NULL);
\r
4576 PromotionPopup(hwndMain);
\r
4580 LoadGameDialog(HWND hwnd, char* title)
\r
4584 char fileTitle[MSG_SIZ];
\r
4585 f = OpenFileDialog(hwnd, "rb", "",
\r
4586 appData.oldSaveStyle ? "gam" : "pgn",
\r
4588 title, &number, fileTitle, NULL);
\r
4590 cmailMsgLoaded = FALSE;
\r
4591 if (number == 0) {
\r
4592 int error = GameListBuild(f);
\r
4594 DisplayError(_("Cannot build game list"), error);
\r
4595 } else if (!ListEmpty(&gameList) &&
\r
4596 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4597 GameListPopUp(f, fileTitle);
\r
4600 GameListDestroy();
\r
4603 LoadGame(f, number, fileTitle, FALSE);
\r
4607 int get_term_width()
\r
4612 HFONT hfont, hold_font;
\r
4617 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4621 // get the text metrics
\r
4622 hdc = GetDC(hText);
\r
4623 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4624 if (consoleCF.dwEffects & CFE_BOLD)
\r
4625 lf.lfWeight = FW_BOLD;
\r
4626 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4627 lf.lfItalic = TRUE;
\r
4628 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4629 lf.lfStrikeOut = TRUE;
\r
4630 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4631 lf.lfUnderline = TRUE;
\r
4632 hfont = CreateFontIndirect(&lf);
\r
4633 hold_font = SelectObject(hdc, hfont);
\r
4634 GetTextMetrics(hdc, &tm);
\r
4635 SelectObject(hdc, hold_font);
\r
4636 DeleteObject(hfont);
\r
4637 ReleaseDC(hText, hdc);
\r
4639 // get the rectangle
\r
4640 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4642 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4645 void UpdateICSWidth(HWND hText)
\r
4647 LONG old_width, new_width;
\r
4649 new_width = get_term_width(hText, FALSE);
\r
4650 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4651 if (new_width != old_width)
\r
4653 ics_update_width(new_width);
\r
4654 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4659 ChangedConsoleFont()
\r
4662 CHARRANGE tmpsel, sel;
\r
4663 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4664 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4665 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4668 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4669 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4670 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4671 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4672 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4673 * size. This was undocumented in the version of MSVC++ that I had
\r
4674 * when I wrote the code, but is apparently documented now.
\r
4676 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4677 cfmt.bCharSet = f->lf.lfCharSet;
\r
4678 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4679 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4680 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4681 /* Why are the following seemingly needed too? */
\r
4682 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4683 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4684 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4686 tmpsel.cpMax = -1; /*999999?*/
\r
4687 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4688 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4689 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4690 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4692 paraf.cbSize = sizeof(paraf);
\r
4693 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4694 paraf.dxStartIndent = 0;
\r
4695 paraf.dxOffset = WRAP_INDENT;
\r
4696 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4697 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4698 UpdateICSWidth(hText);
\r
4701 /*---------------------------------------------------------------------------*\
\r
4703 * Window Proc for main window
\r
4705 \*---------------------------------------------------------------------------*/
\r
4707 /* Process messages for main window, etc. */
\r
4709 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4712 int wmId, wmEvent;
\r
4716 char fileTitle[MSG_SIZ];
\r
4717 static SnapData sd;
\r
4718 static int peek=0;
\r
4720 switch (message) {
\r
4722 case WM_PAINT: /* message: repaint portion of window */
\r
4726 case WM_ERASEBKGND:
\r
4727 if (IsIconic(hwnd)) {
\r
4728 /* Cheat; change the message */
\r
4729 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4731 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4735 case WM_LBUTTONDOWN:
\r
4736 case WM_MBUTTONDOWN:
\r
4737 case WM_RBUTTONDOWN:
\r
4738 case WM_LBUTTONUP:
\r
4739 case WM_MBUTTONUP:
\r
4740 case WM_RBUTTONUP:
\r
4741 case WM_MOUSEMOVE:
\r
4742 case WM_MOUSEWHEEL:
\r
4743 MouseEvent(hwnd, message, wParam, lParam);
\r
4747 if((char)wParam == '\b') {
\r
4748 ForwardEvent(); peek = 0;
\r
4751 JAWS_KBUP_NAVIGATION
\r
4756 if((char)wParam == '\b') {
\r
4757 if(!peek) BackwardEvent(), peek = 1;
\r
4760 JAWS_KBDOWN_NAVIGATION
\r
4766 JAWS_ALT_INTERCEPT
\r
4768 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4769 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4770 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4771 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4773 SendMessage(h, message, wParam, lParam);
\r
4774 } else if(lParam != KF_REPEAT) {
\r
4775 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4776 TypeInEvent((char)wParam);
\r
4777 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4778 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4783 case WM_PALETTECHANGED:
\r
4784 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4786 HDC hdc = GetDC(hwndMain);
\r
4787 SelectPalette(hdc, hPal, TRUE);
\r
4788 nnew = RealizePalette(hdc);
\r
4790 paletteChanged = TRUE;
\r
4791 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4793 ReleaseDC(hwnd, hdc);
\r
4797 case WM_QUERYNEWPALETTE:
\r
4798 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4800 HDC hdc = GetDC(hwndMain);
\r
4801 paletteChanged = FALSE;
\r
4802 SelectPalette(hdc, hPal, FALSE);
\r
4803 nnew = RealizePalette(hdc);
\r
4805 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4807 ReleaseDC(hwnd, hdc);
\r
4812 case WM_COMMAND: /* message: command from application menu */
\r
4813 wmId = LOWORD(wParam);
\r
4814 wmEvent = HIWORD(wParam);
\r
4819 SAY("new game enter a move to play against the computer with white");
\r
4822 case IDM_NewGameFRC:
\r
4823 if( NewGameFRC() == 0 ) {
\r
4828 case IDM_NewVariant:
\r
4829 NewVariantPopup(hwnd);
\r
4832 case IDM_LoadGame:
\r
4833 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4836 case IDM_LoadNextGame:
\r
4840 case IDM_LoadPrevGame:
\r
4844 case IDM_ReloadGame:
\r
4848 case IDM_LoadPosition:
\r
4849 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4850 Reset(FALSE, TRUE);
\r
4853 f = OpenFileDialog(hwnd, "rb", "",
\r
4854 appData.oldSaveStyle ? "pos" : "fen",
\r
4856 _("Load Position from File"), &number, fileTitle, NULL);
\r
4858 LoadPosition(f, number, fileTitle);
\r
4862 case IDM_LoadNextPosition:
\r
4863 ReloadPosition(1);
\r
4866 case IDM_LoadPrevPosition:
\r
4867 ReloadPosition(-1);
\r
4870 case IDM_ReloadPosition:
\r
4871 ReloadPosition(0);
\r
4874 case IDM_SaveGame:
\r
4875 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4876 f = OpenFileDialog(hwnd, "a", defName,
\r
4877 appData.oldSaveStyle ? "gam" : "pgn",
\r
4879 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4881 SaveGame(f, 0, "");
\r
4885 case IDM_SavePosition:
\r
4886 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4887 f = OpenFileDialog(hwnd, "a", defName,
\r
4888 appData.oldSaveStyle ? "pos" : "fen",
\r
4890 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4892 SavePosition(f, 0, "");
\r
4896 case IDM_SaveDiagram:
\r
4897 defName = "diagram";
\r
4898 f = OpenFileDialog(hwnd, "wb", defName,
\r
4901 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4907 case IDM_CreateBook:
\r
4908 CreateBookEvent();
\r
4911 case IDM_CopyGame:
\r
4912 CopyGameToClipboard();
\r
4915 case IDM_PasteGame:
\r
4916 PasteGameFromClipboard();
\r
4919 case IDM_CopyGameListToClipboard:
\r
4920 CopyGameListToClipboard();
\r
4923 /* [AS] Autodetect FEN or PGN data */
\r
4924 case IDM_PasteAny:
\r
4925 PasteGameOrFENFromClipboard();
\r
4928 /* [AS] Move history */
\r
4929 case IDM_ShowMoveHistory:
\r
4930 if( MoveHistoryIsUp() ) {
\r
4931 MoveHistoryPopDown();
\r
4934 MoveHistoryPopUp();
\r
4938 /* [AS] Eval graph */
\r
4939 case IDM_ShowEvalGraph:
\r
4940 if( EvalGraphIsUp() ) {
\r
4941 EvalGraphPopDown();
\r
4945 SetFocus(hwndMain);
\r
4949 /* [AS] Engine output */
\r
4950 case IDM_ShowEngineOutput:
\r
4951 if( EngineOutputIsUp() ) {
\r
4952 EngineOutputPopDown();
\r
4955 EngineOutputPopUp();
\r
4959 /* [AS] User adjudication */
\r
4960 case IDM_UserAdjudication_White:
\r
4961 UserAdjudicationEvent( +1 );
\r
4964 case IDM_UserAdjudication_Black:
\r
4965 UserAdjudicationEvent( -1 );
\r
4968 case IDM_UserAdjudication_Draw:
\r
4969 UserAdjudicationEvent( 0 );
\r
4972 /* [AS] Game list options dialog */
\r
4973 case IDM_GameListOptions:
\r
4974 GameListOptions();
\r
4981 case IDM_CopyPosition:
\r
4982 CopyFENToClipboard();
\r
4985 case IDM_PastePosition:
\r
4986 PasteFENFromClipboard();
\r
4989 case IDM_MailMove:
\r
4993 case IDM_ReloadCMailMsg:
\r
4994 Reset(TRUE, TRUE);
\r
4995 ReloadCmailMsgEvent(FALSE);
\r
4998 case IDM_Minimize:
\r
4999 ShowWindow(hwnd, SW_MINIMIZE);
\r
5006 case IDM_MachineWhite:
\r
5007 MachineWhiteEvent();
\r
5009 * refresh the tags dialog only if it's visible
\r
5011 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5013 tags = PGNTags(&gameInfo);
\r
5014 TagsPopUp(tags, CmailMsg());
\r
5017 SAY("computer starts playing white");
\r
5020 case IDM_MachineBlack:
\r
5021 MachineBlackEvent();
\r
5023 * refresh the tags dialog only if it's visible
\r
5025 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5027 tags = PGNTags(&gameInfo);
\r
5028 TagsPopUp(tags, CmailMsg());
\r
5031 SAY("computer starts playing black");
\r
5034 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5035 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5038 case IDM_TwoMachines:
\r
5039 TwoMachinesEvent();
\r
5041 * refresh the tags dialog only if it's visible
\r
5043 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5045 tags = PGNTags(&gameInfo);
\r
5046 TagsPopUp(tags, CmailMsg());
\r
5049 SAY("computer starts playing both sides");
\r
5052 case IDM_AnalysisMode:
\r
5053 if(AnalyzeModeEvent()) {
\r
5054 SAY("analyzing current position");
\r
5058 case IDM_AnalyzeFile:
\r
5059 AnalyzeFileEvent();
\r
5062 case IDM_IcsClient:
\r
5066 case IDM_EditGame:
\r
5067 case IDM_EditGame2:
\r
5072 case IDM_EditPosition:
\r
5073 case IDM_EditPosition2:
\r
5074 EditPositionEvent();
\r
5075 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5078 case IDM_Training:
\r
5082 case IDM_ShowGameList:
\r
5083 ShowGameListProc();
\r
5086 case IDM_EditProgs1:
\r
5087 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5090 case IDM_LoadProg1:
\r
5091 LoadEnginePopUp(hwndMain, 0);
\r
5094 case IDM_LoadProg2:
\r
5095 LoadEnginePopUp(hwndMain, 1);
\r
5098 case IDM_EditServers:
\r
5099 EditTagsPopUp(icsNames, &icsNames);
\r
5102 case IDM_EditTags:
\r
5107 case IDM_EditBook:
\r
5111 case IDM_EditComment:
\r
5113 if (commentUp && editComment) {
\r
5116 EditCommentEvent();
\r
5136 case IDM_CallFlag:
\r
5156 case IDM_StopObserving:
\r
5157 StopObservingEvent();
\r
5160 case IDM_StopExamining:
\r
5161 StopExaminingEvent();
\r
5165 UploadGameEvent();
\r
5168 case IDM_TypeInMove:
\r
5169 TypeInEvent('\000');
\r
5172 case IDM_TypeInName:
\r
5173 PopUpNameDialog('\000');
\r
5176 case IDM_Backward:
\r
5178 SetFocus(hwndMain);
\r
5185 SetFocus(hwndMain);
\r
5190 SetFocus(hwndMain);
\r
5195 SetFocus(hwndMain);
\r
5198 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5199 case OPT_GameListPrev:
\r
5200 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5204 RevertEvent(FALSE);
\r
5207 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5208 RevertEvent(TRUE);
\r
5211 case IDM_TruncateGame:
\r
5212 TruncateGameEvent();
\r
5219 case IDM_RetractMove:
\r
5220 RetractMoveEvent();
\r
5223 case IDM_FlipView:
\r
5224 flipView = !flipView;
\r
5225 DrawPosition(FALSE, NULL);
\r
5228 case IDM_FlipClock:
\r
5229 flipClock = !flipClock;
\r
5230 DisplayBothClocks();
\r
5234 case IDM_MuteSounds:
\r
5235 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5236 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5237 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5240 case IDM_GeneralOptions:
\r
5241 GeneralOptionsPopup(hwnd);
\r
5242 DrawPosition(TRUE, NULL);
\r
5245 case IDM_BoardOptions:
\r
5246 BoardOptionsPopup(hwnd);
\r
5249 case IDM_ThemeOptions:
\r
5250 ThemeOptionsPopup(hwnd);
\r
5253 case IDM_EnginePlayOptions:
\r
5254 EnginePlayOptionsPopup(hwnd);
\r
5257 case IDM_Engine1Options:
\r
5258 EngineOptionsPopup(hwnd, &first);
\r
5261 case IDM_Engine2Options:
\r
5263 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5264 EngineOptionsPopup(hwnd, &second);
\r
5267 case IDM_OptionsUCI:
\r
5268 UciOptionsPopup(hwnd);
\r
5272 TourneyPopup(hwnd);
\r
5275 case IDM_IcsOptions:
\r
5276 IcsOptionsPopup(hwnd);
\r
5280 FontsOptionsPopup(hwnd);
\r
5284 SoundOptionsPopup(hwnd);
\r
5287 case IDM_CommPort:
\r
5288 CommPortOptionsPopup(hwnd);
\r
5291 case IDM_LoadOptions:
\r
5292 LoadOptionsPopup(hwnd);
\r
5295 case IDM_SaveOptions:
\r
5296 SaveOptionsPopup(hwnd);
\r
5299 case IDM_TimeControl:
\r
5300 TimeControlOptionsPopup(hwnd);
\r
5303 case IDM_SaveSettings:
\r
5304 SaveSettings(settingsFileName);
\r
5307 case IDM_SaveSettingsOnExit:
\r
5308 saveSettingsOnExit = !saveSettingsOnExit;
\r
5309 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5310 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5311 MF_CHECKED : MF_UNCHECKED));
\r
5322 case IDM_AboutGame:
\r
5327 appData.debugMode = !appData.debugMode;
\r
5328 if (appData.debugMode) {
\r
5329 char dir[MSG_SIZ];
\r
5330 GetCurrentDirectory(MSG_SIZ, dir);
\r
5331 SetCurrentDirectory(installDir);
\r
5332 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5333 SetCurrentDirectory(dir);
\r
5334 setbuf(debugFP, NULL);
\r
5341 case IDM_HELPCONTENTS:
\r
5342 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5343 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5344 MessageBox (GetFocus(),
\r
5345 _("Unable to activate help"),
\r
5346 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5350 case IDM_HELPSEARCH:
\r
5351 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5352 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5353 MessageBox (GetFocus(),
\r
5354 _("Unable to activate help"),
\r
5355 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5359 case IDM_HELPHELP:
\r
5360 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5361 MessageBox (GetFocus(),
\r
5362 _("Unable to activate help"),
\r
5363 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5368 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5370 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5371 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5372 FreeProcInstance(lpProc);
\r
5375 case IDM_DirectCommand1:
\r
5376 AskQuestionEvent(_("Direct Command"),
\r
5377 _("Send to chess program:"), "", "1");
\r
5379 case IDM_DirectCommand2:
\r
5380 AskQuestionEvent(_("Direct Command"),
\r
5381 _("Send to second chess program:"), "", "2");
\r
5384 case EP_WhitePawn:
\r
5385 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5389 case EP_WhiteKnight:
\r
5390 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5394 case EP_WhiteBishop:
\r
5395 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5396 fromX = fromY = -1;
\r
5399 case EP_WhiteRook:
\r
5400 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5401 fromX = fromY = -1;
\r
5404 case EP_WhiteQueen:
\r
5405 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5406 fromX = fromY = -1;
\r
5409 case EP_WhiteFerz:
\r
5410 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5411 fromX = fromY = -1;
\r
5414 case EP_WhiteWazir:
\r
5415 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5416 fromX = fromY = -1;
\r
5419 case EP_WhiteAlfil:
\r
5420 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5421 fromX = fromY = -1;
\r
5424 case EP_WhiteCannon:
\r
5425 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5426 fromX = fromY = -1;
\r
5429 case EP_WhiteCardinal:
\r
5430 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5431 fromX = fromY = -1;
\r
5434 case EP_WhiteMarshall:
\r
5435 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5436 fromX = fromY = -1;
\r
5439 case EP_WhiteKing:
\r
5440 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5441 fromX = fromY = -1;
\r
5444 case EP_BlackPawn:
\r
5445 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5446 fromX = fromY = -1;
\r
5449 case EP_BlackKnight:
\r
5450 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5451 fromX = fromY = -1;
\r
5454 case EP_BlackBishop:
\r
5455 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5456 fromX = fromY = -1;
\r
5459 case EP_BlackRook:
\r
5460 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5461 fromX = fromY = -1;
\r
5464 case EP_BlackQueen:
\r
5465 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5466 fromX = fromY = -1;
\r
5469 case EP_BlackFerz:
\r
5470 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5471 fromX = fromY = -1;
\r
5474 case EP_BlackWazir:
\r
5475 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5476 fromX = fromY = -1;
\r
5479 case EP_BlackAlfil:
\r
5480 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5481 fromX = fromY = -1;
\r
5484 case EP_BlackCannon:
\r
5485 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5486 fromX = fromY = -1;
\r
5489 case EP_BlackCardinal:
\r
5490 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5491 fromX = fromY = -1;
\r
5494 case EP_BlackMarshall:
\r
5495 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5496 fromX = fromY = -1;
\r
5499 case EP_BlackKing:
\r
5500 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5501 fromX = fromY = -1;
\r
5504 case EP_EmptySquare:
\r
5505 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5506 fromX = fromY = -1;
\r
5509 case EP_ClearBoard:
\r
5510 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5511 fromX = fromY = -1;
\r
5515 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5516 fromX = fromY = -1;
\r
5520 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5521 fromX = fromY = -1;
\r
5525 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5526 fromX = fromY = -1;
\r
5530 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5531 fromX = fromY = -1;
\r
5535 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5536 fromX = fromY = -1;
\r
5540 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5541 fromX = fromY = -1;
\r
5545 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5546 fromX = fromY = -1;
\r
5550 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5551 fromX = fromY = -1;
\r
5555 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5556 fromX = fromY = -1;
\r
5560 barbaric = 0; appData.language = "";
\r
5561 TranslateMenus(0);
\r
5562 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5563 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5564 lastChecked = wmId;
\r
5568 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5569 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5571 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5572 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5573 TranslateMenus(0);
\r
5574 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5575 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5576 lastChecked = wmId;
\r
5579 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5585 case CLOCK_TIMER_ID:
\r
5586 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5587 clockTimerEvent = 0;
\r
5588 DecrementClocks(); /* call into back end */
\r
5590 case LOAD_GAME_TIMER_ID:
\r
5591 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5592 loadGameTimerEvent = 0;
\r
5593 AutoPlayGameLoop(); /* call into back end */
\r
5595 case ANALYSIS_TIMER_ID:
\r
5596 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5597 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5598 AnalysisPeriodicEvent(0);
\r
5600 KillTimer(hwnd, analysisTimerEvent);
\r
5601 analysisTimerEvent = 0;
\r
5604 case DELAYED_TIMER_ID:
\r
5605 KillTimer(hwnd, delayedTimerEvent);
\r
5606 delayedTimerEvent = 0;
\r
5607 delayedTimerCallback();
\r
5612 case WM_USER_Input:
\r
5613 InputEvent(hwnd, message, wParam, lParam);
\r
5616 /* [AS] Also move "attached" child windows */
\r
5617 case WM_WINDOWPOSCHANGING:
\r
5619 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5620 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5622 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5623 /* Window is moving */
\r
5626 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5627 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5628 rcMain.right = wpMain.x + wpMain.width;
\r
5629 rcMain.top = wpMain.y;
\r
5630 rcMain.bottom = wpMain.y + wpMain.height;
\r
5632 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5633 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5634 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5635 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5636 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5637 wpMain.x = lpwp->x;
\r
5638 wpMain.y = lpwp->y;
\r
5643 /* [AS] Snapping */
\r
5644 case WM_ENTERSIZEMOVE:
\r
5645 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5646 if (hwnd == hwndMain) {
\r
5647 doingSizing = TRUE;
\r
5650 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5654 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5655 if (hwnd == hwndMain) {
\r
5656 lastSizing = wParam;
\r
5661 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5662 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5664 case WM_EXITSIZEMOVE:
\r
5665 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5666 if (hwnd == hwndMain) {
\r
5668 doingSizing = FALSE;
\r
5669 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5670 GetClientRect(hwnd, &client);
\r
5671 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5673 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5675 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5678 case WM_DESTROY: /* message: window being destroyed */
\r
5679 PostQuitMessage(0);
\r
5683 if (hwnd == hwndMain) {
\r
5688 default: /* Passes it on if unprocessed */
\r
5689 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5694 /*---------------------------------------------------------------------------*\
\r
5696 * Misc utility routines
\r
5698 \*---------------------------------------------------------------------------*/
\r
5701 * Decent random number generator, at least not as bad as Windows
\r
5702 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5704 unsigned int randstate;
\r
5709 randstate = randstate * 1664525 + 1013904223;
\r
5710 return (int) randstate & 0x7fffffff;
\r
5714 mysrandom(unsigned int seed)
\r
5721 * returns TRUE if user selects a different color, FALSE otherwise
\r
5725 ChangeColor(HWND hwnd, COLORREF *which)
\r
5727 static BOOL firstTime = TRUE;
\r
5728 static DWORD customColors[16];
\r
5730 COLORREF newcolor;
\r
5735 /* Make initial colors in use available as custom colors */
\r
5736 /* Should we put the compiled-in defaults here instead? */
\r
5738 customColors[i++] = lightSquareColor & 0xffffff;
\r
5739 customColors[i++] = darkSquareColor & 0xffffff;
\r
5740 customColors[i++] = whitePieceColor & 0xffffff;
\r
5741 customColors[i++] = blackPieceColor & 0xffffff;
\r
5742 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5743 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5745 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5746 customColors[i++] = textAttribs[ccl].color;
\r
5748 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5749 firstTime = FALSE;
\r
5752 cc.lStructSize = sizeof(cc);
\r
5753 cc.hwndOwner = hwnd;
\r
5754 cc.hInstance = NULL;
\r
5755 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5756 cc.lpCustColors = (LPDWORD) customColors;
\r
5757 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5759 if (!ChooseColor(&cc)) return FALSE;
\r
5761 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5762 if (newcolor == *which) return FALSE;
\r
5763 *which = newcolor;
\r
5767 InitDrawingColors();
\r
5768 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5773 MyLoadSound(MySound *ms)
\r
5779 if (ms->data && ms->flag) free(ms->data);
\r
5782 switch (ms->name[0]) {
\r
5788 /* System sound from Control Panel. Don't preload here. */
\r
5792 if (ms->name[1] == NULLCHAR) {
\r
5793 /* "!" alone = silence */
\r
5796 /* Builtin wave resource. Error if not found. */
\r
5797 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5798 if (h == NULL) break;
\r
5799 ms->data = (void *)LoadResource(hInst, h);
\r
5800 ms->flag = 0; // not maloced, so cannot be freed!
\r
5801 if (h == NULL) break;
\r
5806 /* .wav file. Error if not found. */
\r
5807 f = fopen(ms->name, "rb");
\r
5808 if (f == NULL) break;
\r
5809 if (fstat(fileno(f), &st) < 0) break;
\r
5810 ms->data = malloc(st.st_size);
\r
5812 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5818 char buf[MSG_SIZ];
\r
5819 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5820 DisplayError(buf, GetLastError());
\r
5826 MyPlaySound(MySound *ms)
\r
5828 BOOLEAN ok = FALSE;
\r
5830 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5831 switch (ms->name[0]) {
\r
5833 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5838 /* System sound from Control Panel (deprecated feature).
\r
5839 "$" alone or an unset sound name gets default beep (still in use). */
\r
5840 if (ms->name[1]) {
\r
5841 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5843 if (!ok) ok = MessageBeep(MB_OK);
\r
5846 /* Builtin wave resource, or "!" alone for silence */
\r
5847 if (ms->name[1]) {
\r
5848 if (ms->data == NULL) return FALSE;
\r
5849 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5855 /* .wav file. Error if not found. */
\r
5856 if (ms->data == NULL) return FALSE;
\r
5857 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5860 /* Don't print an error: this can happen innocently if the sound driver
\r
5861 is busy; for instance, if another instance of WinBoard is playing
\r
5862 a sound at about the same time. */
\r
5868 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5871 OPENFILENAME *ofn;
\r
5872 static UINT *number; /* gross that this is static */
\r
5874 switch (message) {
\r
5875 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5876 /* Center the dialog over the application window */
\r
5877 ofn = (OPENFILENAME *) lParam;
\r
5878 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5879 number = (UINT *) ofn->lCustData;
\r
5880 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5884 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5885 Translate(hDlg, 1536);
\r
5886 return FALSE; /* Allow for further processing */
\r
5889 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5890 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5892 return FALSE; /* Allow for further processing */
\r
5898 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5900 static UINT *number;
\r
5901 OPENFILENAME *ofname;
\r
5904 case WM_INITDIALOG:
\r
5905 Translate(hdlg, DLG_IndexNumber);
\r
5906 ofname = (OPENFILENAME *)lParam;
\r
5907 number = (UINT *)(ofname->lCustData);
\r
5910 ofnot = (OFNOTIFY *)lParam;
\r
5911 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5912 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5921 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5922 char *nameFilt, char *dlgTitle, UINT *number,
\r
5923 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5925 OPENFILENAME openFileName;
\r
5926 char buf1[MSG_SIZ];
\r
5929 if (fileName == NULL) fileName = buf1;
\r
5930 if (defName == NULL) {
\r
5931 safeStrCpy(fileName, "*.", 3 );
\r
5932 strcat(fileName, defExt);
\r
5934 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5936 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5937 if (number) *number = 0;
\r
5939 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5940 openFileName.hwndOwner = hwnd;
\r
5941 openFileName.hInstance = (HANDLE) hInst;
\r
5942 openFileName.lpstrFilter = nameFilt;
\r
5943 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5944 openFileName.nMaxCustFilter = 0L;
\r
5945 openFileName.nFilterIndex = 1L;
\r
5946 openFileName.lpstrFile = fileName;
\r
5947 openFileName.nMaxFile = MSG_SIZ;
\r
5948 openFileName.lpstrFileTitle = fileTitle;
\r
5949 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5950 openFileName.lpstrInitialDir = NULL;
\r
5951 openFileName.lpstrTitle = dlgTitle;
\r
5952 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5953 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5954 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5955 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5956 openFileName.nFileOffset = 0;
\r
5957 openFileName.nFileExtension = 0;
\r
5958 openFileName.lpstrDefExt = defExt;
\r
5959 openFileName.lCustData = (LONG) number;
\r
5960 openFileName.lpfnHook = oldDialog ?
\r
5961 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5962 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5964 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5965 GetOpenFileName(&openFileName)) {
\r
5966 /* open the file */
\r
5967 f = fopen(openFileName.lpstrFile, write);
\r
5969 MessageBox(hwnd, _("File open failed"), NULL,
\r
5970 MB_OK|MB_ICONEXCLAMATION);
\r
5974 int err = CommDlgExtendedError();
\r
5975 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5984 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5986 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5989 * Get the first pop-up menu in the menu template. This is the
\r
5990 * menu that TrackPopupMenu displays.
\r
5992 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5993 TranslateOneMenu(10, hmenuTrackPopup);
\r
5995 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5998 * TrackPopup uses screen coordinates, so convert the
\r
5999 * coordinates of the mouse click to screen coordinates.
\r
6001 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6003 /* Draw and track the floating pop-up menu. */
\r
6004 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6005 pt.x, pt.y, 0, hwnd, NULL);
\r
6007 /* Destroy the menu.*/
\r
6008 DestroyMenu(hmenu);
\r
6013 int sizeX, sizeY, newSizeX, newSizeY;
\r
6015 } ResizeEditPlusButtonsClosure;
\r
6018 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6020 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6024 if (hChild == cl->hText) return TRUE;
\r
6025 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6026 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6027 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6028 ScreenToClient(cl->hDlg, &pt);
\r
6029 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6030 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6034 /* Resize a dialog that has a (rich) edit field filling most of
\r
6035 the top, with a row of buttons below */
\r
6037 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6040 int newTextHeight, newTextWidth;
\r
6041 ResizeEditPlusButtonsClosure cl;
\r
6043 /*if (IsIconic(hDlg)) return;*/
\r
6044 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6046 cl.hdwp = BeginDeferWindowPos(8);
\r
6048 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6049 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6050 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6051 if (newTextHeight < 0) {
\r
6052 newSizeY += -newTextHeight;
\r
6053 newTextHeight = 0;
\r
6055 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6056 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6062 cl.newSizeX = newSizeX;
\r
6063 cl.newSizeY = newSizeY;
\r
6064 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6066 EndDeferWindowPos(cl.hdwp);
\r
6069 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6071 RECT rChild, rParent;
\r
6072 int wChild, hChild, wParent, hParent;
\r
6073 int wScreen, hScreen, xNew, yNew;
\r
6076 /* Get the Height and Width of the child window */
\r
6077 GetWindowRect (hwndChild, &rChild);
\r
6078 wChild = rChild.right - rChild.left;
\r
6079 hChild = rChild.bottom - rChild.top;
\r
6081 /* Get the Height and Width of the parent window */
\r
6082 GetWindowRect (hwndParent, &rParent);
\r
6083 wParent = rParent.right - rParent.left;
\r
6084 hParent = rParent.bottom - rParent.top;
\r
6086 /* Get the display limits */
\r
6087 hdc = GetDC (hwndChild);
\r
6088 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6089 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6090 ReleaseDC(hwndChild, hdc);
\r
6092 /* Calculate new X position, then adjust for screen */
\r
6093 xNew = rParent.left + ((wParent - wChild) /2);
\r
6096 } else if ((xNew+wChild) > wScreen) {
\r
6097 xNew = wScreen - wChild;
\r
6100 /* Calculate new Y position, then adjust for screen */
\r
6102 yNew = rParent.top + ((hParent - hChild) /2);
\r
6105 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6110 } else if ((yNew+hChild) > hScreen) {
\r
6111 yNew = hScreen - hChild;
\r
6114 /* Set it, and return */
\r
6115 return SetWindowPos (hwndChild, NULL,
\r
6116 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6119 /* Center one window over another */
\r
6120 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6122 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6125 /*---------------------------------------------------------------------------*\
\r
6127 * Startup Dialog functions
\r
6129 \*---------------------------------------------------------------------------*/
\r
6131 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6133 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6135 while (*cd != NULL) {
\r
6136 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6142 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6144 char buf1[MAX_ARG_LEN];
\r
6147 if (str[0] == '@') {
\r
6148 FILE* f = fopen(str + 1, "r");
\r
6150 DisplayFatalError(str + 1, errno, 2);
\r
6153 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6155 buf1[len] = NULLCHAR;
\r
6159 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6162 char buf[MSG_SIZ];
\r
6163 char *end = strchr(str, '\n');
\r
6164 if (end == NULL) return;
\r
6165 memcpy(buf, str, end - str);
\r
6166 buf[end - str] = NULLCHAR;
\r
6167 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6173 SetStartupDialogEnables(HWND hDlg)
\r
6175 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6176 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6177 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6178 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6179 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6180 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6181 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6182 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6183 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6184 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6185 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6186 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6187 IsDlgButtonChecked(hDlg, OPT_View));
\r
6191 QuoteForFilename(char *filename)
\r
6193 int dquote, space;
\r
6194 dquote = strchr(filename, '"') != NULL;
\r
6195 space = strchr(filename, ' ') != NULL;
\r
6196 if (dquote || space) {
\r
6208 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6210 char buf[MSG_SIZ];
\r
6213 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6214 q = QuoteForFilename(nthcp);
\r
6215 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6216 if (*nthdir != NULLCHAR) {
\r
6217 q = QuoteForFilename(nthdir);
\r
6218 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6220 if (*nthcp == NULLCHAR) {
\r
6221 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6222 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6223 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6224 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6229 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6231 char buf[MSG_SIZ];
\r
6235 switch (message) {
\r
6236 case WM_INITDIALOG:
\r
6237 /* Center the dialog */
\r
6238 CenterWindow (hDlg, GetDesktopWindow());
\r
6239 Translate(hDlg, DLG_Startup);
\r
6240 /* Initialize the dialog items */
\r
6241 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6242 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6243 firstChessProgramNames);
\r
6244 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6245 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6246 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6247 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6248 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6249 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6250 if (*appData.icsHelper != NULLCHAR) {
\r
6251 char *q = QuoteForFilename(appData.icsHelper);
\r
6252 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6254 if (*appData.icsHost == NULLCHAR) {
\r
6255 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6256 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6257 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6258 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6259 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6262 if (appData.icsActive) {
\r
6263 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6265 else if (appData.noChessProgram) {
\r
6266 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6269 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6272 SetStartupDialogEnables(hDlg);
\r
6276 switch (LOWORD(wParam)) {
\r
6278 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6279 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6280 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6282 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6283 ParseArgs(StringGet, &p);
\r
6284 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6285 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6287 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6288 ParseArgs(StringGet, &p);
\r
6289 SwapEngines(singleList); // ... and then make it 'second'
\r
6290 appData.noChessProgram = FALSE;
\r
6291 appData.icsActive = FALSE;
\r
6292 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6293 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6294 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6296 ParseArgs(StringGet, &p);
\r
6297 if (appData.zippyPlay) {
\r
6298 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6299 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6301 ParseArgs(StringGet, &p);
\r
6303 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6304 appData.noChessProgram = TRUE;
\r
6305 appData.icsActive = FALSE;
\r
6307 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6308 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6311 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6312 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6314 ParseArgs(StringGet, &p);
\r
6316 EndDialog(hDlg, TRUE);
\r
6323 case IDM_HELPCONTENTS:
\r
6324 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6325 MessageBox (GetFocus(),
\r
6326 _("Unable to activate help"),
\r
6327 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6332 SetStartupDialogEnables(hDlg);
\r
6340 /*---------------------------------------------------------------------------*\
\r
6342 * About box dialog functions
\r
6344 \*---------------------------------------------------------------------------*/
\r
6346 /* Process messages for "About" dialog box */
\r
6348 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6350 switch (message) {
\r
6351 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6352 /* Center the dialog over the application window */
\r
6353 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6354 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6355 Translate(hDlg, ABOUTBOX);
\r
6359 case WM_COMMAND: /* message: received a command */
\r
6360 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6361 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6362 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6370 /*---------------------------------------------------------------------------*\
\r
6372 * Comment Dialog functions
\r
6374 \*---------------------------------------------------------------------------*/
\r
6377 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6379 static HANDLE hwndText = NULL;
\r
6380 int len, newSizeX, newSizeY, flags;
\r
6381 static int sizeX, sizeY;
\r
6386 switch (message) {
\r
6387 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6388 /* Initialize the dialog items */
\r
6389 Translate(hDlg, DLG_EditComment);
\r
6390 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6391 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6392 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6393 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6394 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6395 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6396 SetWindowText(hDlg, commentTitle);
\r
6397 if (editComment) {
\r
6398 SetFocus(hwndText);
\r
6400 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6402 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6403 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6404 MAKELPARAM(FALSE, 0));
\r
6405 /* Size and position the dialog */
\r
6406 if (!commentDialog) {
\r
6407 commentDialog = hDlg;
\r
6408 flags = SWP_NOZORDER;
\r
6409 GetClientRect(hDlg, &rect);
\r
6410 sizeX = rect.right;
\r
6411 sizeY = rect.bottom;
\r
6412 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6413 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6414 WINDOWPLACEMENT wp;
\r
6415 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6416 wp.length = sizeof(WINDOWPLACEMENT);
\r
6418 wp.showCmd = SW_SHOW;
\r
6419 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6420 wp.rcNormalPosition.left = wpComment.x;
\r
6421 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6422 wp.rcNormalPosition.top = wpComment.y;
\r
6423 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6424 SetWindowPlacement(hDlg, &wp);
\r
6426 GetClientRect(hDlg, &rect);
\r
6427 newSizeX = rect.right;
\r
6428 newSizeY = rect.bottom;
\r
6429 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6430 newSizeX, newSizeY);
\r
6435 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6438 case WM_COMMAND: /* message: received a command */
\r
6439 switch (LOWORD(wParam)) {
\r
6441 if (editComment) {
\r
6443 /* Read changed options from the dialog box */
\r
6444 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6445 len = GetWindowTextLength(hwndText);
\r
6446 str = (char *) malloc(len + 1);
\r
6447 GetWindowText(hwndText, str, len + 1);
\r
6456 ReplaceComment(commentIndex, str);
\r
6463 case OPT_CancelComment:
\r
6467 case OPT_ClearComment:
\r
6468 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6471 case OPT_EditComment:
\r
6472 EditCommentEvent();
\r
6480 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6481 if( wParam == OPT_CommentText ) {
\r
6482 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6484 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6485 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6489 pt.x = LOWORD( lpMF->lParam );
\r
6490 pt.y = HIWORD( lpMF->lParam );
\r
6492 if(lpMF->msg == WM_CHAR) {
\r
6494 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6495 index = sel.cpMin;
\r
6497 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6499 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6500 len = GetWindowTextLength(hwndText);
\r
6501 str = (char *) malloc(len + 1);
\r
6502 GetWindowText(hwndText, str, len + 1);
\r
6503 ReplaceComment(commentIndex, str);
\r
6504 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6505 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6508 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6509 lpMF->msg = WM_USER;
\r
6517 newSizeX = LOWORD(lParam);
\r
6518 newSizeY = HIWORD(lParam);
\r
6519 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6524 case WM_GETMINMAXINFO:
\r
6525 /* Prevent resizing window too small */
\r
6526 mmi = (MINMAXINFO *) lParam;
\r
6527 mmi->ptMinTrackSize.x = 100;
\r
6528 mmi->ptMinTrackSize.y = 100;
\r
6535 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6540 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6542 if (str == NULL) str = "";
\r
6543 p = (char *) malloc(2 * strlen(str) + 2);
\r
6546 if (*str == '\n') *q++ = '\r';
\r
6550 if (commentText != NULL) free(commentText);
\r
6552 commentIndex = index;
\r
6553 commentTitle = title;
\r
6555 editComment = edit;
\r
6557 if (commentDialog) {
\r
6558 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6559 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6561 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6562 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6563 hwndMain, (DLGPROC)lpProc);
\r
6564 FreeProcInstance(lpProc);
\r
6570 /*---------------------------------------------------------------------------*\
\r
6572 * Type-in move dialog functions
\r
6574 \*---------------------------------------------------------------------------*/
\r
6577 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6579 char move[MSG_SIZ];
\r
6582 switch (message) {
\r
6583 case WM_INITDIALOG:
\r
6584 move[0] = (char) lParam;
\r
6585 move[1] = NULLCHAR;
\r
6586 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6587 Translate(hDlg, DLG_TypeInMove);
\r
6588 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6589 SetWindowText(hInput, move);
\r
6591 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6595 switch (LOWORD(wParam)) {
\r
6598 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6599 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6600 TypeInDoneEvent(move);
\r
6601 EndDialog(hDlg, TRUE);
\r
6604 EndDialog(hDlg, FALSE);
\r
6615 PopUpMoveDialog(char firstchar)
\r
6619 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6620 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6621 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6622 FreeProcInstance(lpProc);
\r
6625 /*---------------------------------------------------------------------------*\
\r
6627 * Type-in name dialog functions
\r
6629 \*---------------------------------------------------------------------------*/
\r
6632 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6634 char move[MSG_SIZ];
\r
6637 switch (message) {
\r
6638 case WM_INITDIALOG:
\r
6639 move[0] = (char) lParam;
\r
6640 move[1] = NULLCHAR;
\r
6641 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6642 Translate(hDlg, DLG_TypeInName);
\r
6643 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6644 SetWindowText(hInput, move);
\r
6646 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6650 switch (LOWORD(wParam)) {
\r
6652 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6653 appData.userName = strdup(move);
\r
6656 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6657 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6658 DisplayTitle(move);
\r
6662 EndDialog(hDlg, TRUE);
\r
6665 EndDialog(hDlg, FALSE);
\r
6676 PopUpNameDialog(char firstchar)
\r
6680 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6681 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6682 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6683 FreeProcInstance(lpProc);
\r
6686 /*---------------------------------------------------------------------------*\
\r
6690 \*---------------------------------------------------------------------------*/
\r
6692 /* Nonmodal error box */
\r
6693 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6694 WPARAM wParam, LPARAM lParam);
\r
6697 ErrorPopUp(char *title, char *content)
\r
6701 BOOLEAN modal = hwndMain == NULL;
\r
6719 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6720 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6723 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6725 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6726 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6727 hwndMain, (DLGPROC)lpProc);
\r
6728 FreeProcInstance(lpProc);
\r
6735 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6736 if (errorDialog == NULL) return;
\r
6737 DestroyWindow(errorDialog);
\r
6738 errorDialog = NULL;
\r
6739 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6743 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6748 switch (message) {
\r
6749 case WM_INITDIALOG:
\r
6750 GetWindowRect(hDlg, &rChild);
\r
6753 SetWindowPos(hDlg, NULL, rChild.left,
\r
6754 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6755 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6759 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6760 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6761 and it doesn't work when you resize the dialog.
\r
6762 For now, just give it a default position.
\r
6764 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6765 Translate(hDlg, DLG_Error);
\r
6767 errorDialog = hDlg;
\r
6768 SetWindowText(hDlg, errorTitle);
\r
6769 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6770 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6774 switch (LOWORD(wParam)) {
\r
6777 if (errorDialog == hDlg) errorDialog = NULL;
\r
6778 DestroyWindow(hDlg);
\r
6790 HWND gothicDialog = NULL;
\r
6793 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6797 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6799 switch (message) {
\r
6800 case WM_INITDIALOG:
\r
6801 GetWindowRect(hDlg, &rChild);
\r
6803 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6807 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6808 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6809 and it doesn't work when you resize the dialog.
\r
6810 For now, just give it a default position.
\r
6812 gothicDialog = hDlg;
\r
6813 SetWindowText(hDlg, errorTitle);
\r
6814 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6815 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6819 switch (LOWORD(wParam)) {
\r
6822 if (errorDialog == hDlg) errorDialog = NULL;
\r
6823 DestroyWindow(hDlg);
\r
6835 GothicPopUp(char *title, VariantClass variant)
\r
6838 static char *lastTitle;
\r
6840 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6841 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6843 if(lastTitle != title && gothicDialog != NULL) {
\r
6844 DestroyWindow(gothicDialog);
\r
6845 gothicDialog = NULL;
\r
6847 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6848 title = lastTitle;
\r
6849 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6850 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6851 hwndMain, (DLGPROC)lpProc);
\r
6852 FreeProcInstance(lpProc);
\r
6857 /*---------------------------------------------------------------------------*\
\r
6859 * Ics Interaction console functions
\r
6861 \*---------------------------------------------------------------------------*/
\r
6863 #define HISTORY_SIZE 64
\r
6864 static char *history[HISTORY_SIZE];
\r
6865 int histIn = 0, histP = 0;
\r
6868 SaveInHistory(char *cmd)
\r
6870 if (history[histIn] != NULL) {
\r
6871 free(history[histIn]);
\r
6872 history[histIn] = NULL;
\r
6874 if (*cmd == NULLCHAR) return;
\r
6875 history[histIn] = StrSave(cmd);
\r
6876 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6877 if (history[histIn] != NULL) {
\r
6878 free(history[histIn]);
\r
6879 history[histIn] = NULL;
\r
6885 PrevInHistory(char *cmd)
\r
6888 if (histP == histIn) {
\r
6889 if (history[histIn] != NULL) free(history[histIn]);
\r
6890 history[histIn] = StrSave(cmd);
\r
6892 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6893 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6895 return history[histP];
\r
6901 if (histP == histIn) return NULL;
\r
6902 histP = (histP + 1) % HISTORY_SIZE;
\r
6903 return history[histP];
\r
6907 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6911 hmenu = LoadMenu(hInst, "TextMenu");
\r
6912 h = GetSubMenu(hmenu, 0);
\r
6914 if (strcmp(e->item, "-") == 0) {
\r
6915 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6916 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6917 int flags = MF_STRING, j = 0;
\r
6918 if (e->item[0] == '|') {
\r
6919 flags |= MF_MENUBARBREAK;
\r
6922 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6923 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6931 WNDPROC consoleTextWindowProc;
\r
6934 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6936 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6937 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6941 SetWindowText(hInput, command);
\r
6943 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6945 sel.cpMin = 999999;
\r
6946 sel.cpMax = 999999;
\r
6947 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6952 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6953 if (sel.cpMin == sel.cpMax) {
\r
6954 /* Expand to surrounding word */
\r
6957 tr.chrg.cpMax = sel.cpMin;
\r
6958 tr.chrg.cpMin = --sel.cpMin;
\r
6959 if (sel.cpMin < 0) break;
\r
6960 tr.lpstrText = name;
\r
6961 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6962 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6966 tr.chrg.cpMin = sel.cpMax;
\r
6967 tr.chrg.cpMax = ++sel.cpMax;
\r
6968 tr.lpstrText = name;
\r
6969 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6970 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6973 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6974 MessageBeep(MB_ICONEXCLAMATION);
\r
6978 tr.lpstrText = name;
\r
6979 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6981 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6982 MessageBeep(MB_ICONEXCLAMATION);
\r
6985 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6988 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6989 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6990 SetWindowText(hInput, buf);
\r
6991 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6993 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6994 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6995 SetWindowText(hInput, buf);
\r
6996 sel.cpMin = 999999;
\r
6997 sel.cpMax = 999999;
\r
6998 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7004 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7009 switch (message) {
\r
7011 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7012 if(wParam=='R') return 0;
\r
7015 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7018 sel.cpMin = 999999;
\r
7019 sel.cpMax = 999999;
\r
7020 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7021 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7026 if(wParam != '\022') {
\r
7027 if (wParam == '\t') {
\r
7028 if (GetKeyState(VK_SHIFT) < 0) {
\r
7030 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7031 if (buttonDesc[0].hwnd) {
\r
7032 SetFocus(buttonDesc[0].hwnd);
\r
7034 SetFocus(hwndMain);
\r
7038 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7041 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7042 JAWS_DELETE( SetFocus(hInput); )
\r
7043 SendMessage(hInput, message, wParam, lParam);
\r
7046 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7048 case WM_RBUTTONDOWN:
\r
7049 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7050 /* Move selection here if it was empty */
\r
7052 pt.x = LOWORD(lParam);
\r
7053 pt.y = HIWORD(lParam);
\r
7054 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7055 if (sel.cpMin == sel.cpMax) {
\r
7056 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7057 sel.cpMax = sel.cpMin;
\r
7058 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7060 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7061 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7063 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7064 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7065 if (sel.cpMin == sel.cpMax) {
\r
7066 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7067 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7069 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7070 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7072 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7073 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7074 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7075 MenuPopup(hwnd, pt, hmenu, -1);
\r
7079 case WM_RBUTTONUP:
\r
7080 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7081 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7082 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7086 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7088 return SendMessage(hInput, message, wParam, lParam);
\r
7089 case WM_MBUTTONDOWN:
\r
7090 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7092 switch (LOWORD(wParam)) {
\r
7093 case IDM_QuickPaste:
\r
7095 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7096 if (sel.cpMin == sel.cpMax) {
\r
7097 MessageBeep(MB_ICONEXCLAMATION);
\r
7100 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7101 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7102 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7107 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7110 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7113 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7117 int i = LOWORD(wParam) - IDM_CommandX;
\r
7118 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7119 icsTextMenuEntry[i].command != NULL) {
\r
7120 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7121 icsTextMenuEntry[i].getname,
\r
7122 icsTextMenuEntry[i].immediate);
\r
7130 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7133 WNDPROC consoleInputWindowProc;
\r
7136 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7138 char buf[MSG_SIZ];
\r
7140 static BOOL sendNextChar = FALSE;
\r
7141 static BOOL quoteNextChar = FALSE;
\r
7142 InputSource *is = consoleInputSource;
\r
7146 switch (message) {
\r
7148 if (!appData.localLineEditing || sendNextChar) {
\r
7149 is->buf[0] = (CHAR) wParam;
\r
7151 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7152 sendNextChar = FALSE;
\r
7155 if (quoteNextChar) {
\r
7156 buf[0] = (char) wParam;
\r
7157 buf[1] = NULLCHAR;
\r
7158 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7159 quoteNextChar = FALSE;
\r
7163 case '\r': /* Enter key */
\r
7164 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7165 if (consoleEcho) SaveInHistory(is->buf);
\r
7166 is->buf[is->count++] = '\n';
\r
7167 is->buf[is->count] = NULLCHAR;
\r
7168 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7169 if (consoleEcho) {
\r
7170 ConsoleOutput(is->buf, is->count, TRUE);
\r
7171 } else if (appData.localLineEditing) {
\r
7172 ConsoleOutput("\n", 1, TRUE);
\r
7175 case '\033': /* Escape key */
\r
7176 SetWindowText(hwnd, "");
\r
7177 cf.cbSize = sizeof(CHARFORMAT);
\r
7178 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7179 if (consoleEcho) {
\r
7180 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7182 cf.crTextColor = COLOR_ECHOOFF;
\r
7184 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7185 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7187 case '\t': /* Tab key */
\r
7188 if (GetKeyState(VK_SHIFT) < 0) {
\r
7190 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7193 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7194 if (buttonDesc[0].hwnd) {
\r
7195 SetFocus(buttonDesc[0].hwnd);
\r
7197 SetFocus(hwndMain);
\r
7201 case '\023': /* Ctrl+S */
\r
7202 sendNextChar = TRUE;
\r
7204 case '\021': /* Ctrl+Q */
\r
7205 quoteNextChar = TRUE;
\r
7215 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7216 p = PrevInHistory(buf);
\r
7218 SetWindowText(hwnd, p);
\r
7219 sel.cpMin = 999999;
\r
7220 sel.cpMax = 999999;
\r
7221 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7226 p = NextInHistory();
\r
7228 SetWindowText(hwnd, p);
\r
7229 sel.cpMin = 999999;
\r
7230 sel.cpMax = 999999;
\r
7231 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7237 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7241 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7245 case WM_MBUTTONDOWN:
\r
7246 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7247 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7249 case WM_RBUTTONUP:
\r
7250 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7251 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7252 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7256 hmenu = LoadMenu(hInst, "InputMenu");
\r
7257 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7258 if (sel.cpMin == sel.cpMax) {
\r
7259 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7260 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7262 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7263 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7265 pt.x = LOWORD(lParam);
\r
7266 pt.y = HIWORD(lParam);
\r
7267 MenuPopup(hwnd, pt, hmenu, -1);
\r
7271 switch (LOWORD(wParam)) {
\r
7273 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7275 case IDM_SelectAll:
\r
7277 sel.cpMax = -1; /*999999?*/
\r
7278 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7281 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7284 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7287 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7292 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7295 #define CO_MAX 100000
\r
7296 #define CO_TRIM 1000
\r
7299 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7301 static SnapData sd;
\r
7302 HWND hText, hInput;
\r
7304 static int sizeX, sizeY;
\r
7305 int newSizeX, newSizeY;
\r
7309 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7310 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7312 switch (message) {
\r
7314 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7316 ENLINK *pLink = (ENLINK*)lParam;
\r
7317 if (pLink->msg == WM_LBUTTONUP)
\r
7321 tr.chrg = pLink->chrg;
\r
7322 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7323 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7324 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7325 free(tr.lpstrText);
\r
7329 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7330 hwndConsole = hDlg;
\r
7332 consoleTextWindowProc = (WNDPROC)
\r
7333 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7334 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7335 consoleInputWindowProc = (WNDPROC)
\r
7336 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7337 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7338 Colorize(ColorNormal, TRUE);
\r
7339 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7340 ChangedConsoleFont();
\r
7341 GetClientRect(hDlg, &rect);
\r
7342 sizeX = rect.right;
\r
7343 sizeY = rect.bottom;
\r
7344 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7345 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7346 WINDOWPLACEMENT wp;
\r
7347 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7348 wp.length = sizeof(WINDOWPLACEMENT);
\r
7350 wp.showCmd = SW_SHOW;
\r
7351 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7352 wp.rcNormalPosition.left = wpConsole.x;
\r
7353 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7354 wp.rcNormalPosition.top = wpConsole.y;
\r
7355 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7356 SetWindowPlacement(hDlg, &wp);
\r
7359 // [HGM] Chessknight's change 2004-07-13
\r
7360 else { /* Determine Defaults */
\r
7361 WINDOWPLACEMENT wp;
\r
7362 wpConsole.x = wpMain.width + 1;
\r
7363 wpConsole.y = wpMain.y;
\r
7364 wpConsole.width = screenWidth - wpMain.width;
\r
7365 wpConsole.height = wpMain.height;
\r
7366 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7367 wp.length = sizeof(WINDOWPLACEMENT);
\r
7369 wp.showCmd = SW_SHOW;
\r
7370 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7371 wp.rcNormalPosition.left = wpConsole.x;
\r
7372 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7373 wp.rcNormalPosition.top = wpConsole.y;
\r
7374 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7375 SetWindowPlacement(hDlg, &wp);
\r
7378 // Allow hText to highlight URLs and send notifications on them
\r
7379 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7380 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7381 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7382 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7396 if (IsIconic(hDlg)) break;
\r
7397 newSizeX = LOWORD(lParam);
\r
7398 newSizeY = HIWORD(lParam);
\r
7399 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7400 RECT rectText, rectInput;
\r
7402 int newTextHeight, newTextWidth;
\r
7403 GetWindowRect(hText, &rectText);
\r
7404 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7405 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7406 if (newTextHeight < 0) {
\r
7407 newSizeY += -newTextHeight;
\r
7408 newTextHeight = 0;
\r
7410 SetWindowPos(hText, NULL, 0, 0,
\r
7411 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7412 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7413 pt.x = rectInput.left;
\r
7414 pt.y = rectInput.top + newSizeY - sizeY;
\r
7415 ScreenToClient(hDlg, &pt);
\r
7416 SetWindowPos(hInput, NULL,
\r
7417 pt.x, pt.y, /* needs client coords */
\r
7418 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7419 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7425 case WM_GETMINMAXINFO:
\r
7426 /* Prevent resizing window too small */
\r
7427 mmi = (MINMAXINFO *) lParam;
\r
7428 mmi->ptMinTrackSize.x = 100;
\r
7429 mmi->ptMinTrackSize.y = 100;
\r
7432 /* [AS] Snapping */
\r
7433 case WM_ENTERSIZEMOVE:
\r
7434 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7437 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7440 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7442 case WM_EXITSIZEMOVE:
\r
7443 UpdateICSWidth(hText);
\r
7444 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7447 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7455 if (hwndConsole) return;
\r
7456 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7457 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7462 ConsoleOutput(char* data, int length, int forceVisible)
\r
7467 char buf[CO_MAX+1];
\r
7470 static int delayLF = 0;
\r
7471 CHARRANGE savesel, sel;
\r
7473 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7481 while (length--) {
\r
7489 } else if (*p == '\007') {
\r
7490 MyPlaySound(&sounds[(int)SoundBell]);
\r
7497 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7498 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7499 /* Save current selection */
\r
7500 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7501 exlen = GetWindowTextLength(hText);
\r
7502 /* Find out whether current end of text is visible */
\r
7503 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7504 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7505 /* Trim existing text if it's too long */
\r
7506 if (exlen + (q - buf) > CO_MAX) {
\r
7507 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7510 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7511 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7513 savesel.cpMin -= trim;
\r
7514 savesel.cpMax -= trim;
\r
7515 if (exlen < 0) exlen = 0;
\r
7516 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7517 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7519 /* Append the new text */
\r
7520 sel.cpMin = exlen;
\r
7521 sel.cpMax = exlen;
\r
7522 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7523 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7524 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7525 if (forceVisible || exlen == 0 ||
\r
7526 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7527 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7528 /* Scroll to make new end of text visible if old end of text
\r
7529 was visible or new text is an echo of user typein */
\r
7530 sel.cpMin = 9999999;
\r
7531 sel.cpMax = 9999999;
\r
7532 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7533 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7534 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7535 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7537 if (savesel.cpMax == exlen || forceVisible) {
\r
7538 /* Move insert point to new end of text if it was at the old
\r
7539 end of text or if the new text is an echo of user typein */
\r
7540 sel.cpMin = 9999999;
\r
7541 sel.cpMax = 9999999;
\r
7542 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7544 /* Restore previous selection */
\r
7545 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7547 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7554 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7558 COLORREF oldFg, oldBg;
\r
7562 if(copyNumber > 1)
\r
7563 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7565 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7566 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7567 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7570 rect.right = x + squareSize;
\r
7572 rect.bottom = y + squareSize;
\r
7575 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7576 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7577 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7578 &rect, str, strlen(str), NULL);
\r
7580 (void) SetTextColor(hdc, oldFg);
\r
7581 (void) SetBkColor(hdc, oldBg);
\r
7582 (void) SelectObject(hdc, oldFont);
\r
7586 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7587 RECT *rect, char *color, char *flagFell)
\r
7591 COLORREF oldFg, oldBg;
\r
7594 if (twoBoards && partnerUp) return;
\r
7595 if (appData.clockMode) {
\r
7597 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7599 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7606 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7607 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7609 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7610 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7612 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7616 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7617 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7618 rect, str, strlen(str), NULL);
\r
7619 if(logoHeight > 0 && appData.clockMode) {
\r
7621 str += strlen(color)+2;
\r
7622 r.top = rect->top + logoHeight/2;
\r
7623 r.left = rect->left;
\r
7624 r.right = rect->right;
\r
7625 r.bottom = rect->bottom;
\r
7626 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7627 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7628 &r, str, strlen(str), NULL);
\r
7630 (void) SetTextColor(hdc, oldFg);
\r
7631 (void) SetBkColor(hdc, oldBg);
\r
7632 (void) SelectObject(hdc, oldFont);
\r
7637 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7643 if( count <= 0 ) {
\r
7644 if (appData.debugMode) {
\r
7645 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7648 return ERROR_INVALID_USER_BUFFER;
\r
7651 ResetEvent(ovl->hEvent);
\r
7652 ovl->Offset = ovl->OffsetHigh = 0;
\r
7653 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7657 err = GetLastError();
\r
7658 if (err == ERROR_IO_PENDING) {
\r
7659 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7663 err = GetLastError();
\r
7670 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7675 ResetEvent(ovl->hEvent);
\r
7676 ovl->Offset = ovl->OffsetHigh = 0;
\r
7677 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7681 err = GetLastError();
\r
7682 if (err == ERROR_IO_PENDING) {
\r
7683 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7687 err = GetLastError();
\r
7693 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7694 void CheckForInputBufferFull( InputSource * is )
\r
7696 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7697 /* Look for end of line */
\r
7698 char * p = is->buf;
\r
7700 while( p < is->next && *p != '\n' ) {
\r
7704 if( p >= is->next ) {
\r
7705 if (appData.debugMode) {
\r
7706 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7709 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7710 is->count = (DWORD) -1;
\r
7711 is->next = is->buf;
\r
7717 InputThread(LPVOID arg)
\r
7722 is = (InputSource *) arg;
\r
7723 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7724 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7725 while (is->hThread != NULL) {
\r
7726 is->error = DoReadFile(is->hFile, is->next,
\r
7727 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7728 &is->count, &ovl);
\r
7729 if (is->error == NO_ERROR) {
\r
7730 is->next += is->count;
\r
7732 if (is->error == ERROR_BROKEN_PIPE) {
\r
7733 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7736 is->count = (DWORD) -1;
\r
7737 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7742 CheckForInputBufferFull( is );
\r
7744 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7746 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7748 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7751 CloseHandle(ovl.hEvent);
\r
7752 CloseHandle(is->hFile);
\r
7754 if (appData.debugMode) {
\r
7755 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7762 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7764 NonOvlInputThread(LPVOID arg)
\r
7771 is = (InputSource *) arg;
\r
7772 while (is->hThread != NULL) {
\r
7773 is->error = ReadFile(is->hFile, is->next,
\r
7774 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7775 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7776 if (is->error == NO_ERROR) {
\r
7777 /* Change CRLF to LF */
\r
7778 if (is->next > is->buf) {
\r
7780 i = is->count + 1;
\r
7788 if (prev == '\r' && *p == '\n') {
\r
7800 if (is->error == ERROR_BROKEN_PIPE) {
\r
7801 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7804 is->count = (DWORD) -1;
\r
7808 CheckForInputBufferFull( is );
\r
7810 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7812 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7814 if (is->count < 0) break; /* Quit on error */
\r
7816 CloseHandle(is->hFile);
\r
7821 SocketInputThread(LPVOID arg)
\r
7825 is = (InputSource *) arg;
\r
7826 while (is->hThread != NULL) {
\r
7827 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7828 if ((int)is->count == SOCKET_ERROR) {
\r
7829 is->count = (DWORD) -1;
\r
7830 is->error = WSAGetLastError();
\r
7832 is->error = NO_ERROR;
\r
7833 is->next += is->count;
\r
7834 if (is->count == 0 && is->second == is) {
\r
7835 /* End of file on stderr; quit with no message */
\r
7839 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7841 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7843 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7849 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7853 is = (InputSource *) lParam;
\r
7854 if (is->lineByLine) {
\r
7855 /* Feed in lines one by one */
\r
7856 char *p = is->buf;
\r
7858 while (q < is->next) {
\r
7859 if (*q++ == '\n') {
\r
7860 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7865 /* Move any partial line to the start of the buffer */
\r
7867 while (p < is->next) {
\r
7872 if (is->error != NO_ERROR || is->count == 0) {
\r
7873 /* Notify backend of the error. Note: If there was a partial
\r
7874 line at the end, it is not flushed through. */
\r
7875 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7878 /* Feed in the whole chunk of input at once */
\r
7879 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7880 is->next = is->buf;
\r
7884 /*---------------------------------------------------------------------------*\
\r
7886 * Menu enables. Used when setting various modes.
\r
7888 \*---------------------------------------------------------------------------*/
\r
7896 GreyRevert(Boolean grey)
\r
7897 { // [HGM] vari: for retracting variations in local mode
\r
7898 HMENU hmenu = GetMenu(hwndMain);
\r
7899 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7900 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7904 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7906 while (enab->item > 0) {
\r
7907 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7912 Enables gnuEnables[] = {
\r
7913 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7927 // Needed to switch from ncp to GNU mode on Engine Load
\r
7928 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7929 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7936 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7937 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7940 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7941 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7945 Enables icsEnables[] = {
\r
7946 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7954 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7962 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7970 Enables zippyEnables[] = {
\r
7971 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7972 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7973 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7979 Enables ncpEnables[] = {
\r
7980 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7987 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7988 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7989 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7990 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7991 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7992 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7993 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7994 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8005 Enables trainingOnEnables[] = {
\r
8006 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8018 Enables trainingOffEnables[] = {
\r
8019 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8020 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8021 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8025 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8026 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8027 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8031 /* These modify either ncpEnables or gnuEnables */
\r
8032 Enables cmailEnables[] = {
\r
8033 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8034 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8036 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8043 Enables machineThinkingEnables[] = {
\r
8044 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8050 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8051 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8052 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8054 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8055 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8056 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8057 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8058 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8059 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8063 Enables userThinkingEnables[] = {
\r
8064 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8065 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8066 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8067 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8069 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8070 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8071 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8072 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8073 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8074 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8075 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8076 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8077 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8078 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8079 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8083 /*---------------------------------------------------------------------------*\
\r
8085 * Front-end interface functions exported by XBoard.
\r
8086 * Functions appear in same order as prototypes in frontend.h.
\r
8088 \*---------------------------------------------------------------------------*/
\r
8090 CheckMark(UINT item, int state)
\r
8092 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8098 static UINT prevChecked = 0;
\r
8099 static int prevPausing = 0;
\r
8102 if (pausing != prevPausing) {
\r
8103 prevPausing = pausing;
\r
8104 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8105 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8106 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8109 switch (gameMode) {
\r
8110 case BeginningOfGame:
\r
8111 if (appData.icsActive)
\r
8112 nowChecked = IDM_IcsClient;
\r
8113 else if (appData.noChessProgram)
\r
8114 nowChecked = IDM_EditGame;
\r
8116 nowChecked = IDM_MachineBlack;
\r
8118 case MachinePlaysBlack:
\r
8119 nowChecked = IDM_MachineBlack;
\r
8121 case MachinePlaysWhite:
\r
8122 nowChecked = IDM_MachineWhite;
\r
8124 case TwoMachinesPlay:
\r
8125 nowChecked = IDM_TwoMachines;
\r
8128 nowChecked = IDM_AnalysisMode;
\r
8131 nowChecked = IDM_AnalyzeFile;
\r
8134 nowChecked = IDM_EditGame;
\r
8136 case PlayFromGameFile:
\r
8137 nowChecked = IDM_LoadGame;
\r
8139 case EditPosition:
\r
8140 nowChecked = IDM_EditPosition;
\r
8143 nowChecked = IDM_Training;
\r
8145 case IcsPlayingWhite:
\r
8146 case IcsPlayingBlack:
\r
8147 case IcsObserving:
\r
8149 nowChecked = IDM_IcsClient;
\r
8156 CheckMark(prevChecked, MF_UNCHECKED);
\r
8157 CheckMark(nowChecked, MF_CHECKED);
\r
8158 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8160 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8161 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8162 MF_BYCOMMAND|MF_ENABLED);
\r
8164 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8165 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8168 prevChecked = nowChecked;
\r
8170 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8171 if (appData.icsActive) {
\r
8172 if (appData.icsEngineAnalyze) {
\r
8173 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8175 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8178 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8184 HMENU hmenu = GetMenu(hwndMain);
\r
8185 SetMenuEnables(hmenu, icsEnables);
\r
8186 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8187 MF_BYCOMMAND|MF_ENABLED);
\r
8189 if (appData.zippyPlay) {
\r
8190 SetMenuEnables(hmenu, zippyEnables);
\r
8191 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8192 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8193 MF_BYCOMMAND|MF_ENABLED);
\r
8201 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8207 HMENU hmenu = GetMenu(hwndMain);
\r
8208 SetMenuEnables(hmenu, ncpEnables);
\r
8209 DrawMenuBar(hwndMain);
\r
8215 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8219 SetTrainingModeOn()
\r
8222 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8223 for (i = 0; i < N_BUTTONS; i++) {
\r
8224 if (buttonDesc[i].hwnd != NULL)
\r
8225 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8230 VOID SetTrainingModeOff()
\r
8233 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8234 for (i = 0; i < N_BUTTONS; i++) {
\r
8235 if (buttonDesc[i].hwnd != NULL)
\r
8236 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8242 SetUserThinkingEnables()
\r
8244 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8248 SetMachineThinkingEnables()
\r
8250 HMENU hMenu = GetMenu(hwndMain);
\r
8251 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8253 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8255 if (gameMode == MachinePlaysBlack) {
\r
8256 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8257 } else if (gameMode == MachinePlaysWhite) {
\r
8258 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8259 } else if (gameMode == TwoMachinesPlay) {
\r
8260 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8266 DisplayTitle(char *str)
\r
8268 char title[MSG_SIZ], *host;
\r
8269 if (str[0] != NULLCHAR) {
\r
8270 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8271 } else if (appData.icsActive) {
\r
8272 if (appData.icsCommPort[0] != NULLCHAR)
\r
8275 host = appData.icsHost;
\r
8276 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8277 } else if (appData.noChessProgram) {
\r
8278 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8280 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8281 strcat(title, ": ");
\r
8282 strcat(title, first.tidy);
\r
8284 SetWindowText(hwndMain, title);
\r
8289 DisplayMessage(char *str1, char *str2)
\r
8293 int remain = MESSAGE_TEXT_MAX - 1;
\r
8296 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8297 messageText[0] = NULLCHAR;
\r
8299 len = strlen(str1);
\r
8300 if (len > remain) len = remain;
\r
8301 strncpy(messageText, str1, len);
\r
8302 messageText[len] = NULLCHAR;
\r
8305 if (*str2 && remain >= 2) {
\r
8307 strcat(messageText, " ");
\r
8310 len = strlen(str2);
\r
8311 if (len > remain) len = remain;
\r
8312 strncat(messageText, str2, len);
\r
8314 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8315 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8317 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8321 hdc = GetDC(hwndMain);
\r
8322 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8323 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8324 &messageRect, messageText, strlen(messageText), NULL);
\r
8325 (void) SelectObject(hdc, oldFont);
\r
8326 (void) ReleaseDC(hwndMain, hdc);
\r
8330 DisplayError(char *str, int error)
\r
8332 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8336 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8338 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8339 NULL, error, LANG_NEUTRAL,
\r
8340 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8342 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8344 ErrorMap *em = errmap;
\r
8345 while (em->err != 0 && em->err != error) em++;
\r
8346 if (em->err != 0) {
\r
8347 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8349 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8354 ErrorPopUp(_("Error"), buf);
\r
8359 DisplayMoveError(char *str)
\r
8361 fromX = fromY = -1;
\r
8362 ClearHighlights();
\r
8363 DrawPosition(FALSE, NULL);
\r
8364 if (appData.popupMoveErrors) {
\r
8365 ErrorPopUp(_("Error"), str);
\r
8367 DisplayMessage(str, "");
\r
8368 moveErrorMessageUp = TRUE;
\r
8373 DisplayFatalError(char *str, int error, int exitStatus)
\r
8375 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8377 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8380 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8381 NULL, error, LANG_NEUTRAL,
\r
8382 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8384 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8386 ErrorMap *em = errmap;
\r
8387 while (em->err != 0 && em->err != error) em++;
\r
8388 if (em->err != 0) {
\r
8389 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8391 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8396 if (appData.debugMode) {
\r
8397 fprintf(debugFP, "%s: %s\n", label, str);
\r
8399 if (appData.popupExitMessage) {
\r
8400 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8401 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8403 ExitEvent(exitStatus);
\r
8408 DisplayInformation(char *str)
\r
8410 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8415 DisplayNote(char *str)
\r
8417 ErrorPopUp(_("Note"), str);
\r
8422 char *title, *question, *replyPrefix;
\r
8427 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8429 static QuestionParams *qp;
\r
8430 char reply[MSG_SIZ];
\r
8433 switch (message) {
\r
8434 case WM_INITDIALOG:
\r
8435 qp = (QuestionParams *) lParam;
\r
8436 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8437 Translate(hDlg, DLG_Question);
\r
8438 SetWindowText(hDlg, qp->title);
\r
8439 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8440 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8444 switch (LOWORD(wParam)) {
\r
8446 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8447 if (*reply) strcat(reply, " ");
\r
8448 len = strlen(reply);
\r
8449 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8450 strcat(reply, "\n");
\r
8451 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8452 EndDialog(hDlg, TRUE);
\r
8453 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8456 EndDialog(hDlg, FALSE);
\r
8467 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8469 QuestionParams qp;
\r
8473 qp.question = question;
\r
8474 qp.replyPrefix = replyPrefix;
\r
8476 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8477 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8478 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8479 FreeProcInstance(lpProc);
\r
8482 /* [AS] Pick FRC position */
\r
8483 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8485 static int * lpIndexFRC;
\r
8491 case WM_INITDIALOG:
\r
8492 lpIndexFRC = (int *) lParam;
\r
8494 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8495 Translate(hDlg, DLG_NewGameFRC);
\r
8497 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8498 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8499 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8500 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8505 switch( LOWORD(wParam) ) {
\r
8507 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8508 EndDialog( hDlg, 0 );
\r
8509 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8512 EndDialog( hDlg, 1 );
\r
8514 case IDC_NFG_Edit:
\r
8515 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8516 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8518 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8521 case IDC_NFG_Random:
\r
8522 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8523 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8536 int index = appData.defaultFrcPosition;
\r
8537 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8539 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8541 if( result == 0 ) {
\r
8542 appData.defaultFrcPosition = index;
\r
8548 /* [AS] Game list options. Refactored by HGM */
\r
8550 HWND gameListOptionsDialog;
\r
8552 // low-level front-end: clear text edit / list widget
\r
8556 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8559 // low-level front-end: clear text edit / list widget
\r
8561 GLT_DeSelectList()
\r
8563 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8566 // low-level front-end: append line to text edit / list widget
\r
8568 GLT_AddToList( char *name )
\r
8571 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8575 // low-level front-end: get line from text edit / list widget
\r
8577 GLT_GetFromList( int index, char *name )
\r
8580 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8586 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8588 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8589 int idx2 = idx1 + delta;
\r
8590 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8592 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8595 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8596 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8597 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8598 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8602 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8606 case WM_INITDIALOG:
\r
8607 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8609 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8610 Translate(hDlg, DLG_GameListOptions);
\r
8612 /* Initialize list */
\r
8613 GLT_TagsToList( lpUserGLT );
\r
8615 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8620 switch( LOWORD(wParam) ) {
\r
8623 EndDialog( hDlg, 0 );
\r
8626 EndDialog( hDlg, 1 );
\r
8629 case IDC_GLT_Default:
\r
8630 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8633 case IDC_GLT_Restore:
\r
8634 GLT_TagsToList( appData.gameListTags );
\r
8638 GLT_MoveSelection( hDlg, -1 );
\r
8641 case IDC_GLT_Down:
\r
8642 GLT_MoveSelection( hDlg, +1 );
\r
8652 int GameListOptions()
\r
8655 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8657 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8659 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8661 if( result == 0 ) {
\r
8662 /* [AS] Memory leak here! */
\r
8663 appData.gameListTags = strdup( lpUserGLT );
\r
8670 DisplayIcsInteractionTitle(char *str)
\r
8672 char consoleTitle[MSG_SIZ];
\r
8674 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8675 SetWindowText(hwndConsole, consoleTitle);
\r
8677 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8678 char buf[MSG_SIZ], *p = buf, *q;
\r
8679 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8681 q = strchr(p, ';');
\r
8683 if(*p) ChatPopUp(p);
\r
8687 SetActiveWindow(hwndMain);
\r
8691 DrawPosition(int fullRedraw, Board board)
\r
8693 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8696 void NotifyFrontendLogin()
\r
8699 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8705 fromX = fromY = -1;
\r
8706 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8707 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8708 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8709 dragInfo.lastpos = dragInfo.pos;
\r
8710 dragInfo.start.x = dragInfo.start.y = -1;
\r
8711 dragInfo.from = dragInfo.start;
\r
8713 DrawPosition(TRUE, NULL);
\r
8720 CommentPopUp(char *title, char *str)
\r
8722 HWND hwnd = GetActiveWindow();
\r
8723 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8725 SetActiveWindow(hwnd);
\r
8729 CommentPopDown(void)
\r
8731 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8732 if (commentDialog) {
\r
8733 ShowWindow(commentDialog, SW_HIDE);
\r
8735 commentUp = FALSE;
\r
8739 EditCommentPopUp(int index, char *title, char *str)
\r
8741 EitherCommentPopUp(index, title, str, TRUE);
\r
8748 MyPlaySound(&sounds[(int)SoundMove]);
\r
8751 VOID PlayIcsWinSound()
\r
8753 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8756 VOID PlayIcsLossSound()
\r
8758 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8761 VOID PlayIcsDrawSound()
\r
8763 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8766 VOID PlayIcsUnfinishedSound()
\r
8768 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8774 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8780 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8788 consoleEcho = TRUE;
\r
8789 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8790 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8791 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8800 consoleEcho = FALSE;
\r
8801 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8802 /* This works OK: set text and background both to the same color */
\r
8804 cf.crTextColor = COLOR_ECHOOFF;
\r
8805 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8806 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8809 /* No Raw()...? */
\r
8811 void Colorize(ColorClass cc, int continuation)
\r
8813 currentColorClass = cc;
\r
8814 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8815 consoleCF.crTextColor = textAttribs[cc].color;
\r
8816 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8817 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8823 static char buf[MSG_SIZ];
\r
8824 DWORD bufsiz = MSG_SIZ;
\r
8826 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8827 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8829 if (!GetUserName(buf, &bufsiz)) {
\r
8830 /*DisplayError("Error getting user name", GetLastError());*/
\r
8831 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8839 static char buf[MSG_SIZ];
\r
8840 DWORD bufsiz = MSG_SIZ;
\r
8842 if (!GetComputerName(buf, &bufsiz)) {
\r
8843 /*DisplayError("Error getting host name", GetLastError());*/
\r
8844 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8851 ClockTimerRunning()
\r
8853 return clockTimerEvent != 0;
\r
8859 if (clockTimerEvent == 0) return FALSE;
\r
8860 KillTimer(hwndMain, clockTimerEvent);
\r
8861 clockTimerEvent = 0;
\r
8866 StartClockTimer(long millisec)
\r
8868 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8869 (UINT) millisec, NULL);
\r
8873 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8876 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8878 if(appData.noGUI) return;
\r
8879 hdc = GetDC(hwndMain);
\r
8880 if (!IsIconic(hwndMain)) {
\r
8881 DisplayAClock(hdc, timeRemaining, highlight,
\r
8882 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8884 if (highlight && iconCurrent == iconBlack) {
\r
8885 iconCurrent = iconWhite;
\r
8886 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8887 if (IsIconic(hwndMain)) {
\r
8888 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8891 (void) ReleaseDC(hwndMain, hdc);
\r
8893 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8897 DisplayBlackClock(long timeRemaining, int highlight)
\r
8900 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8902 if(appData.noGUI) return;
\r
8903 hdc = GetDC(hwndMain);
\r
8904 if (!IsIconic(hwndMain)) {
\r
8905 DisplayAClock(hdc, timeRemaining, highlight,
\r
8906 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8908 if (highlight && iconCurrent == iconWhite) {
\r
8909 iconCurrent = iconBlack;
\r
8910 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8911 if (IsIconic(hwndMain)) {
\r
8912 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8915 (void) ReleaseDC(hwndMain, hdc);
\r
8917 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8922 LoadGameTimerRunning()
\r
8924 return loadGameTimerEvent != 0;
\r
8928 StopLoadGameTimer()
\r
8930 if (loadGameTimerEvent == 0) return FALSE;
\r
8931 KillTimer(hwndMain, loadGameTimerEvent);
\r
8932 loadGameTimerEvent = 0;
\r
8937 StartLoadGameTimer(long millisec)
\r
8939 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8940 (UINT) millisec, NULL);
\r
8948 char fileTitle[MSG_SIZ];
\r
8950 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8951 f = OpenFileDialog(hwndMain, "a", defName,
\r
8952 appData.oldSaveStyle ? "gam" : "pgn",
\r
8954 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8956 SaveGame(f, 0, "");
\r
8963 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8965 if (delayedTimerEvent != 0) {
\r
8966 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8967 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8969 KillTimer(hwndMain, delayedTimerEvent);
\r
8970 delayedTimerEvent = 0;
\r
8971 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8972 delayedTimerCallback();
\r
8974 delayedTimerCallback = cb;
\r
8975 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8976 (UINT) millisec, NULL);
\r
8979 DelayedEventCallback
\r
8982 if (delayedTimerEvent) {
\r
8983 return delayedTimerCallback;
\r
8990 CancelDelayedEvent()
\r
8992 if (delayedTimerEvent) {
\r
8993 KillTimer(hwndMain, delayedTimerEvent);
\r
8994 delayedTimerEvent = 0;
\r
8998 DWORD GetWin32Priority(int nice)
\r
8999 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9001 REALTIME_PRIORITY_CLASS 0x00000100
\r
9002 HIGH_PRIORITY_CLASS 0x00000080
\r
9003 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9004 NORMAL_PRIORITY_CLASS 0x00000020
\r
9005 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9006 IDLE_PRIORITY_CLASS 0x00000040
\r
9008 if (nice < -15) return 0x00000080;
\r
9009 if (nice < 0) return 0x00008000;
\r
9010 if (nice == 0) return 0x00000020;
\r
9011 if (nice < 15) return 0x00004000;
\r
9012 return 0x00000040;
\r
9015 void RunCommand(char *cmdLine)
\r
9017 /* Now create the child process. */
\r
9018 STARTUPINFO siStartInfo;
\r
9019 PROCESS_INFORMATION piProcInfo;
\r
9021 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9022 siStartInfo.lpReserved = NULL;
\r
9023 siStartInfo.lpDesktop = NULL;
\r
9024 siStartInfo.lpTitle = NULL;
\r
9025 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9026 siStartInfo.cbReserved2 = 0;
\r
9027 siStartInfo.lpReserved2 = NULL;
\r
9028 siStartInfo.hStdInput = NULL;
\r
9029 siStartInfo.hStdOutput = NULL;
\r
9030 siStartInfo.hStdError = NULL;
\r
9032 CreateProcess(NULL,
\r
9033 cmdLine, /* command line */
\r
9034 NULL, /* process security attributes */
\r
9035 NULL, /* primary thread security attrs */
\r
9036 TRUE, /* handles are inherited */
\r
9037 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9038 NULL, /* use parent's environment */
\r
9040 &siStartInfo, /* STARTUPINFO pointer */
\r
9041 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9043 CloseHandle(piProcInfo.hThread);
\r
9046 /* Start a child process running the given program.
\r
9047 The process's standard output can be read from "from", and its
\r
9048 standard input can be written to "to".
\r
9049 Exit with fatal error if anything goes wrong.
\r
9050 Returns an opaque pointer that can be used to destroy the process
\r
9054 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9056 #define BUFSIZE 4096
\r
9058 HANDLE hChildStdinRd, hChildStdinWr,
\r
9059 hChildStdoutRd, hChildStdoutWr;
\r
9060 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9061 SECURITY_ATTRIBUTES saAttr;
\r
9063 PROCESS_INFORMATION piProcInfo;
\r
9064 STARTUPINFO siStartInfo;
\r
9066 char buf[MSG_SIZ];
\r
9069 if (appData.debugMode) {
\r
9070 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9075 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9076 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9077 saAttr.bInheritHandle = TRUE;
\r
9078 saAttr.lpSecurityDescriptor = NULL;
\r
9081 * The steps for redirecting child's STDOUT:
\r
9082 * 1. Create anonymous pipe to be STDOUT for child.
\r
9083 * 2. Create a noninheritable duplicate of read handle,
\r
9084 * and close the inheritable read handle.
\r
9087 /* Create a pipe for the child's STDOUT. */
\r
9088 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9089 return GetLastError();
\r
9092 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9093 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9094 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9095 FALSE, /* not inherited */
\r
9096 DUPLICATE_SAME_ACCESS);
\r
9098 return GetLastError();
\r
9100 CloseHandle(hChildStdoutRd);
\r
9103 * The steps for redirecting child's STDIN:
\r
9104 * 1. Create anonymous pipe to be STDIN for child.
\r
9105 * 2. Create a noninheritable duplicate of write handle,
\r
9106 * and close the inheritable write handle.
\r
9109 /* Create a pipe for the child's STDIN. */
\r
9110 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9111 return GetLastError();
\r
9114 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9115 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9116 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9117 FALSE, /* not inherited */
\r
9118 DUPLICATE_SAME_ACCESS);
\r
9120 return GetLastError();
\r
9122 CloseHandle(hChildStdinWr);
\r
9124 /* Arrange to (1) look in dir for the child .exe file, and
\r
9125 * (2) have dir be the child's working directory. Interpret
\r
9126 * dir relative to the directory WinBoard loaded from. */
\r
9127 GetCurrentDirectory(MSG_SIZ, buf);
\r
9128 SetCurrentDirectory(installDir);
\r
9129 SetCurrentDirectory(dir);
\r
9131 /* Now create the child process. */
\r
9133 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9134 siStartInfo.lpReserved = NULL;
\r
9135 siStartInfo.lpDesktop = NULL;
\r
9136 siStartInfo.lpTitle = NULL;
\r
9137 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9138 siStartInfo.cbReserved2 = 0;
\r
9139 siStartInfo.lpReserved2 = NULL;
\r
9140 siStartInfo.hStdInput = hChildStdinRd;
\r
9141 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9142 siStartInfo.hStdError = hChildStdoutWr;
\r
9144 fSuccess = CreateProcess(NULL,
\r
9145 cmdLine, /* command line */
\r
9146 NULL, /* process security attributes */
\r
9147 NULL, /* primary thread security attrs */
\r
9148 TRUE, /* handles are inherited */
\r
9149 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9150 NULL, /* use parent's environment */
\r
9152 &siStartInfo, /* STARTUPINFO pointer */
\r
9153 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9155 err = GetLastError();
\r
9156 SetCurrentDirectory(buf); /* return to prev directory */
\r
9161 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9162 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9163 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9166 /* Close the handles we don't need in the parent */
\r
9167 CloseHandle(piProcInfo.hThread);
\r
9168 CloseHandle(hChildStdinRd);
\r
9169 CloseHandle(hChildStdoutWr);
\r
9171 /* Prepare return value */
\r
9172 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9173 cp->kind = CPReal;
\r
9174 cp->hProcess = piProcInfo.hProcess;
\r
9175 cp->pid = piProcInfo.dwProcessId;
\r
9176 cp->hFrom = hChildStdoutRdDup;
\r
9177 cp->hTo = hChildStdinWrDup;
\r
9179 *pr = (void *) cp;
\r
9181 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9182 2000 where engines sometimes don't see the initial command(s)
\r
9183 from WinBoard and hang. I don't understand how that can happen,
\r
9184 but the Sleep is harmless, so I've put it in. Others have also
\r
9185 reported what may be the same problem, so hopefully this will fix
\r
9186 it for them too. */
\r
9194 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9196 ChildProc *cp; int result;
\r
9198 cp = (ChildProc *) pr;
\r
9199 if (cp == NULL) return;
\r
9201 switch (cp->kind) {
\r
9203 /* TerminateProcess is considered harmful, so... */
\r
9204 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9205 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9206 /* The following doesn't work because the chess program
\r
9207 doesn't "have the same console" as WinBoard. Maybe
\r
9208 we could arrange for this even though neither WinBoard
\r
9209 nor the chess program uses a console for stdio? */
\r
9210 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9212 /* [AS] Special termination modes for misbehaving programs... */
\r
9213 if( signal == 9 ) {
\r
9214 result = TerminateProcess( cp->hProcess, 0 );
\r
9216 if ( appData.debugMode) {
\r
9217 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9220 else if( signal == 10 ) {
\r
9221 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9223 if( dw != WAIT_OBJECT_0 ) {
\r
9224 result = TerminateProcess( cp->hProcess, 0 );
\r
9226 if ( appData.debugMode) {
\r
9227 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9233 CloseHandle(cp->hProcess);
\r
9237 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9241 closesocket(cp->sock);
\r
9246 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9247 closesocket(cp->sock);
\r
9248 closesocket(cp->sock2);
\r
9256 InterruptChildProcess(ProcRef pr)
\r
9260 cp = (ChildProc *) pr;
\r
9261 if (cp == NULL) return;
\r
9262 switch (cp->kind) {
\r
9264 /* The following doesn't work because the chess program
\r
9265 doesn't "have the same console" as WinBoard. Maybe
\r
9266 we could arrange for this even though neither WinBoard
\r
9267 nor the chess program uses a console for stdio */
\r
9268 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9273 /* Can't interrupt */
\r
9277 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9284 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9286 char cmdLine[MSG_SIZ];
\r
9288 if (port[0] == NULLCHAR) {
\r
9289 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9291 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9293 return StartChildProcess(cmdLine, "", pr);
\r
9297 /* Code to open TCP sockets */
\r
9300 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9306 struct sockaddr_in sa, mysa;
\r
9307 struct hostent FAR *hp;
\r
9308 unsigned short uport;
\r
9309 WORD wVersionRequested;
\r
9312 /* Initialize socket DLL */
\r
9313 wVersionRequested = MAKEWORD(1, 1);
\r
9314 err = WSAStartup(wVersionRequested, &wsaData);
\r
9315 if (err != 0) return err;
\r
9318 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9319 err = WSAGetLastError();
\r
9324 /* Bind local address using (mostly) don't-care values.
\r
9326 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9327 mysa.sin_family = AF_INET;
\r
9328 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9329 uport = (unsigned short) 0;
\r
9330 mysa.sin_port = htons(uport);
\r
9331 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9332 == SOCKET_ERROR) {
\r
9333 err = WSAGetLastError();
\r
9338 /* Resolve remote host name */
\r
9339 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9340 if (!(hp = gethostbyname(host))) {
\r
9341 unsigned int b0, b1, b2, b3;
\r
9343 err = WSAGetLastError();
\r
9345 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9346 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9347 hp->h_addrtype = AF_INET;
\r
9349 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9350 hp->h_addr_list[0] = (char *) malloc(4);
\r
9351 hp->h_addr_list[0][0] = (char) b0;
\r
9352 hp->h_addr_list[0][1] = (char) b1;
\r
9353 hp->h_addr_list[0][2] = (char) b2;
\r
9354 hp->h_addr_list[0][3] = (char) b3;
\r
9360 sa.sin_family = hp->h_addrtype;
\r
9361 uport = (unsigned short) atoi(port);
\r
9362 sa.sin_port = htons(uport);
\r
9363 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9365 /* Make connection */
\r
9366 if (connect(s, (struct sockaddr *) &sa,
\r
9367 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9368 err = WSAGetLastError();
\r
9373 /* Prepare return value */
\r
9374 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9375 cp->kind = CPSock;
\r
9377 *pr = (ProcRef *) cp;
\r
9383 OpenCommPort(char *name, ProcRef *pr)
\r
9388 char fullname[MSG_SIZ];
\r
9390 if (*name != '\\')
\r
9391 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9393 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9395 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9396 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9397 if (h == (HANDLE) -1) {
\r
9398 return GetLastError();
\r
9402 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9404 /* Accumulate characters until a 100ms pause, then parse */
\r
9405 ct.ReadIntervalTimeout = 100;
\r
9406 ct.ReadTotalTimeoutMultiplier = 0;
\r
9407 ct.ReadTotalTimeoutConstant = 0;
\r
9408 ct.WriteTotalTimeoutMultiplier = 0;
\r
9409 ct.WriteTotalTimeoutConstant = 0;
\r
9410 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9412 /* Prepare return value */
\r
9413 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9414 cp->kind = CPComm;
\r
9417 *pr = (ProcRef *) cp;
\r
9423 OpenLoopback(ProcRef *pr)
\r
9425 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9431 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9436 struct sockaddr_in sa, mysa;
\r
9437 struct hostent FAR *hp;
\r
9438 unsigned short uport;
\r
9439 WORD wVersionRequested;
\r
9442 char stderrPortStr[MSG_SIZ];
\r
9444 /* Initialize socket DLL */
\r
9445 wVersionRequested = MAKEWORD(1, 1);
\r
9446 err = WSAStartup(wVersionRequested, &wsaData);
\r
9447 if (err != 0) return err;
\r
9449 /* Resolve remote host name */
\r
9450 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9451 if (!(hp = gethostbyname(host))) {
\r
9452 unsigned int b0, b1, b2, b3;
\r
9454 err = WSAGetLastError();
\r
9456 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9457 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9458 hp->h_addrtype = AF_INET;
\r
9460 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9461 hp->h_addr_list[0] = (char *) malloc(4);
\r
9462 hp->h_addr_list[0][0] = (char) b0;
\r
9463 hp->h_addr_list[0][1] = (char) b1;
\r
9464 hp->h_addr_list[0][2] = (char) b2;
\r
9465 hp->h_addr_list[0][3] = (char) b3;
\r
9471 sa.sin_family = hp->h_addrtype;
\r
9472 uport = (unsigned short) 514;
\r
9473 sa.sin_port = htons(uport);
\r
9474 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9476 /* Bind local socket to unused "privileged" port address
\r
9478 s = INVALID_SOCKET;
\r
9479 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9480 mysa.sin_family = AF_INET;
\r
9481 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9482 for (fromPort = 1023;; fromPort--) {
\r
9483 if (fromPort < 0) {
\r
9485 return WSAEADDRINUSE;
\r
9487 if (s == INVALID_SOCKET) {
\r
9488 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9489 err = WSAGetLastError();
\r
9494 uport = (unsigned short) fromPort;
\r
9495 mysa.sin_port = htons(uport);
\r
9496 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9497 == SOCKET_ERROR) {
\r
9498 err = WSAGetLastError();
\r
9499 if (err == WSAEADDRINUSE) continue;
\r
9503 if (connect(s, (struct sockaddr *) &sa,
\r
9504 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9505 err = WSAGetLastError();
\r
9506 if (err == WSAEADDRINUSE) {
\r
9517 /* Bind stderr local socket to unused "privileged" port address
\r
9519 s2 = INVALID_SOCKET;
\r
9520 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9521 mysa.sin_family = AF_INET;
\r
9522 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9523 for (fromPort = 1023;; fromPort--) {
\r
9524 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9525 if (fromPort < 0) {
\r
9526 (void) closesocket(s);
\r
9528 return WSAEADDRINUSE;
\r
9530 if (s2 == INVALID_SOCKET) {
\r
9531 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9532 err = WSAGetLastError();
\r
9538 uport = (unsigned short) fromPort;
\r
9539 mysa.sin_port = htons(uport);
\r
9540 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9541 == SOCKET_ERROR) {
\r
9542 err = WSAGetLastError();
\r
9543 if (err == WSAEADDRINUSE) continue;
\r
9544 (void) closesocket(s);
\r
9548 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9549 err = WSAGetLastError();
\r
9550 if (err == WSAEADDRINUSE) {
\r
9552 s2 = INVALID_SOCKET;
\r
9555 (void) closesocket(s);
\r
9556 (void) closesocket(s2);
\r
9562 prevStderrPort = fromPort; // remember port used
\r
9563 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9565 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9566 err = WSAGetLastError();
\r
9567 (void) closesocket(s);
\r
9568 (void) closesocket(s2);
\r
9573 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9574 err = WSAGetLastError();
\r
9575 (void) closesocket(s);
\r
9576 (void) closesocket(s2);
\r
9580 if (*user == NULLCHAR) user = UserName();
\r
9581 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9582 err = WSAGetLastError();
\r
9583 (void) closesocket(s);
\r
9584 (void) closesocket(s2);
\r
9588 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9589 err = WSAGetLastError();
\r
9590 (void) closesocket(s);
\r
9591 (void) closesocket(s2);
\r
9596 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9597 err = WSAGetLastError();
\r
9598 (void) closesocket(s);
\r
9599 (void) closesocket(s2);
\r
9603 (void) closesocket(s2); /* Stop listening */
\r
9605 /* Prepare return value */
\r
9606 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9607 cp->kind = CPRcmd;
\r
9610 *pr = (ProcRef *) cp;
\r
9617 AddInputSource(ProcRef pr, int lineByLine,
\r
9618 InputCallback func, VOIDSTAR closure)
\r
9620 InputSource *is, *is2 = NULL;
\r
9621 ChildProc *cp = (ChildProc *) pr;
\r
9623 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9624 is->lineByLine = lineByLine;
\r
9626 is->closure = closure;
\r
9627 is->second = NULL;
\r
9628 is->next = is->buf;
\r
9629 if (pr == NoProc) {
\r
9630 is->kind = CPReal;
\r
9631 consoleInputSource = is;
\r
9633 is->kind = cp->kind;
\r
9635 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9636 we create all threads suspended so that the is->hThread variable can be
\r
9637 safely assigned, then let the threads start with ResumeThread.
\r
9639 switch (cp->kind) {
\r
9641 is->hFile = cp->hFrom;
\r
9642 cp->hFrom = NULL; /* now owned by InputThread */
\r
9644 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9645 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9649 is->hFile = cp->hFrom;
\r
9650 cp->hFrom = NULL; /* now owned by InputThread */
\r
9652 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9653 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9657 is->sock = cp->sock;
\r
9659 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9660 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9664 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9666 is->sock = cp->sock;
\r
9668 is2->sock = cp->sock2;
\r
9669 is2->second = is2;
\r
9671 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9672 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9674 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9675 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9679 if( is->hThread != NULL ) {
\r
9680 ResumeThread( is->hThread );
\r
9683 if( is2 != NULL && is2->hThread != NULL ) {
\r
9684 ResumeThread( is2->hThread );
\r
9688 return (InputSourceRef) is;
\r
9692 RemoveInputSource(InputSourceRef isr)
\r
9696 is = (InputSource *) isr;
\r
9697 is->hThread = NULL; /* tell thread to stop */
\r
9698 CloseHandle(is->hThread);
\r
9699 if (is->second != NULL) {
\r
9700 is->second->hThread = NULL;
\r
9701 CloseHandle(is->second->hThread);
\r
9705 int no_wrap(char *message, int count)
\r
9707 ConsoleOutput(message, count, FALSE);
\r
9712 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9715 int outCount = SOCKET_ERROR;
\r
9716 ChildProc *cp = (ChildProc *) pr;
\r
9717 static OVERLAPPED ovl;
\r
9718 static int line = 0;
\r
9722 if (appData.noJoin || !appData.useInternalWrap)
\r
9723 return no_wrap(message, count);
\r
9726 int width = get_term_width();
\r
9727 int len = wrap(NULL, message, count, width, &line);
\r
9728 char *msg = malloc(len);
\r
9732 return no_wrap(message, count);
\r
9735 dbgchk = wrap(msg, message, count, width, &line);
\r
9736 if (dbgchk != len && appData.debugMode)
\r
9737 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9738 ConsoleOutput(msg, len, FALSE);
\r
9745 if (ovl.hEvent == NULL) {
\r
9746 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9748 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9750 switch (cp->kind) {
\r
9753 outCount = send(cp->sock, message, count, 0);
\r
9754 if (outCount == SOCKET_ERROR) {
\r
9755 *outError = WSAGetLastError();
\r
9757 *outError = NO_ERROR;
\r
9762 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9763 &dOutCount, NULL)) {
\r
9764 *outError = NO_ERROR;
\r
9765 outCount = (int) dOutCount;
\r
9767 *outError = GetLastError();
\r
9772 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9773 &dOutCount, &ovl);
\r
9774 if (*outError == NO_ERROR) {
\r
9775 outCount = (int) dOutCount;
\r
9785 if(n != 0) Sleep(n);
\r
9789 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9792 /* Ignore delay, not implemented for WinBoard */
\r
9793 return OutputToProcess(pr, message, count, outError);
\r
9798 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9799 char *buf, int count, int error)
\r
9801 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9804 /* see wgamelist.c for Game List functions */
\r
9805 /* see wedittags.c for Edit Tags functions */
\r
9812 char buf[MSG_SIZ];
\r
9815 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9816 f = fopen(buf, "r");
\r
9818 ProcessICSInitScript(f);
\r
9828 StartAnalysisClock()
\r
9830 if (analysisTimerEvent) return;
\r
9831 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9832 (UINT) 2000, NULL);
\r
9836 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9838 highlightInfo.sq[0].x = fromX;
\r
9839 highlightInfo.sq[0].y = fromY;
\r
9840 highlightInfo.sq[1].x = toX;
\r
9841 highlightInfo.sq[1].y = toY;
\r
9847 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9848 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9852 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9854 premoveHighlightInfo.sq[0].x = fromX;
\r
9855 premoveHighlightInfo.sq[0].y = fromY;
\r
9856 premoveHighlightInfo.sq[1].x = toX;
\r
9857 premoveHighlightInfo.sq[1].y = toY;
\r
9861 ClearPremoveHighlights()
\r
9863 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9864 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9868 ShutDownFrontEnd()
\r
9870 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9871 DeleteClipboardTempFiles();
\r
9877 if (IsIconic(hwndMain))
\r
9878 ShowWindow(hwndMain, SW_RESTORE);
\r
9880 SetActiveWindow(hwndMain);
\r
9884 * Prototypes for animation support routines
\r
9886 static void ScreenSquare(int column, int row, POINT * pt);
\r
9887 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9888 POINT frames[], int * nFrames);
\r
9894 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9895 { // [HGM] atomic: animate blast wave
\r
9898 explodeInfo.fromX = fromX;
\r
9899 explodeInfo.fromY = fromY;
\r
9900 explodeInfo.toX = toX;
\r
9901 explodeInfo.toY = toY;
\r
9902 for(i=1; i<4*kFactor; i++) {
\r
9903 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9904 DrawPosition(FALSE, board);
\r
9905 Sleep(appData.animSpeed);
\r
9907 explodeInfo.radius = 0;
\r
9908 DrawPosition(TRUE, board);
\r
9912 AnimateMove(board, fromX, fromY, toX, toY)
\r
9919 ChessSquare piece;
\r
9920 POINT start, finish, mid;
\r
9921 POINT frames[kFactor * 2 + 1];
\r
9924 if (!appData.animate) return;
\r
9925 if (doingSizing) return;
\r
9926 if (fromY < 0 || fromX < 0) return;
\r
9927 piece = board[fromY][fromX];
\r
9928 if (piece >= EmptySquare) return;
\r
9930 ScreenSquare(fromX, fromY, &start);
\r
9931 ScreenSquare(toX, toY, &finish);
\r
9933 /* All moves except knight jumps move in straight line */
\r
9934 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9935 mid.x = start.x + (finish.x - start.x) / 2;
\r
9936 mid.y = start.y + (finish.y - start.y) / 2;
\r
9938 /* Knight: make straight movement then diagonal */
\r
9939 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9940 mid.x = start.x + (finish.x - start.x) / 2;
\r
9944 mid.y = start.y + (finish.y - start.y) / 2;
\r
9948 /* Don't use as many frames for very short moves */
\r
9949 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9950 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9952 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9954 animInfo.from.x = fromX;
\r
9955 animInfo.from.y = fromY;
\r
9956 animInfo.to.x = toX;
\r
9957 animInfo.to.y = toY;
\r
9958 animInfo.lastpos = start;
\r
9959 animInfo.piece = piece;
\r
9960 for (n = 0; n < nFrames; n++) {
\r
9961 animInfo.pos = frames[n];
\r
9962 DrawPosition(FALSE, NULL);
\r
9963 animInfo.lastpos = animInfo.pos;
\r
9964 Sleep(appData.animSpeed);
\r
9966 animInfo.pos = finish;
\r
9967 DrawPosition(FALSE, NULL);
\r
9968 animInfo.piece = EmptySquare;
\r
9969 Explode(board, fromX, fromY, toX, toY);
\r
9972 /* Convert board position to corner of screen rect and color */
\r
9975 ScreenSquare(column, row, pt)
\r
9976 int column; int row; POINT * pt;
\r
9979 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9980 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9982 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9983 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9987 /* Generate a series of frame coords from start->mid->finish.
\r
9988 The movement rate doubles until the half way point is
\r
9989 reached, then halves back down to the final destination,
\r
9990 which gives a nice slow in/out effect. The algorithmn
\r
9991 may seem to generate too many intermediates for short
\r
9992 moves, but remember that the purpose is to attract the
\r
9993 viewers attention to the piece about to be moved and
\r
9994 then to where it ends up. Too few frames would be less
\r
9998 Tween(start, mid, finish, factor, frames, nFrames)
\r
9999 POINT * start; POINT * mid;
\r
10000 POINT * finish; int factor;
\r
10001 POINT frames[]; int * nFrames;
\r
10003 int n, fraction = 1, count = 0;
\r
10005 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10006 for (n = 0; n < factor; n++)
\r
10008 for (n = 0; n < factor; n++) {
\r
10009 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10010 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10012 fraction = fraction / 2;
\r
10016 frames[count] = *mid;
\r
10019 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10021 for (n = 0; n < factor; n++) {
\r
10022 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10023 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10025 fraction = fraction * 2;
\r
10027 *nFrames = count;
\r
10031 SettingsPopUp(ChessProgramState *cps)
\r
10032 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10033 EngineOptionsPopup(savedHwnd, cps);
\r
10036 int flock(int fid, int code)
\r
10038 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10040 ov.hEvent = NULL;
\r
10042 ov.OffsetHigh = 0;
\r
10044 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10045 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10046 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10047 default: return -1;
\r
10056 static char col[8][20];
\r
10057 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10059 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10064 ActivateTheme (int new)
\r
10065 { // Redo initialization of features depending on options that can occur in themes
\r
10067 if(new) InitDrawingColors();
\r
10068 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10069 InitDrawingSizes(boardSize, 0);
\r
10070 InvalidateRect(hwndMain, NULL, TRUE);
\r