2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize, border;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
190 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
192 ColorClass currentColorClass;
\r
194 static HWND savedHwnd;
\r
195 HWND hCommPort = NULL; /* currently open comm port */
\r
196 static HWND hwndPause; /* pause button */
\r
197 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
198 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
199 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
200 explodeBrush, /* [HGM] atomic */
\r
201 markerBrush[8], /* [HGM] markers */
\r
202 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
203 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
204 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
205 static HPEN gridPen = NULL;
\r
206 static HPEN highlightPen = NULL;
\r
207 static HPEN premovePen = NULL;
\r
208 static NPLOGPALETTE pLogPal;
\r
209 static BOOL paletteChanged = FALSE;
\r
210 static HICON iconWhite, iconBlack, iconCurrent;
\r
211 static int doingSizing = FALSE;
\r
212 static int lastSizing = 0;
\r
213 static int prevStderrPort;
\r
214 static HBITMAP userLogo;
\r
216 static HBITMAP liteBackTexture = NULL;
\r
217 static HBITMAP darkBackTexture = NULL;
\r
218 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
220 static int backTextureSquareSize = 0;
\r
221 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
223 #if __GNUC__ && !defined(_winmajor)
\r
224 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
227 #if defined(_winmajor)
\r
228 #define oldDialog (_winmajor < 4)
\r
230 #define oldDialog 0
\r
234 #define INTERNATIONAL
\r
236 #ifdef INTERNATIONAL
\r
237 # define _(s) T_(s)
\r
243 # define Translate(x, y)
\r
244 # define LoadLanguageFile(s)
\r
247 #ifdef INTERNATIONAL
\r
249 Boolean barbaric; // flag indicating if translation is needed
\r
251 // list of item numbers used in each dialog (used to alter language at run time)
\r
253 #define ABOUTBOX -1 /* not sure why these are needed */
\r
254 #define ABOUTBOX2 -1
\r
256 int dialogItems[][42] = {
\r
257 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
258 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
259 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
260 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
261 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
262 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
263 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
264 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
265 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
266 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
267 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
268 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
269 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
270 { ABOUTBOX2, IDC_ChessBoard },
\r
271 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
272 OPT_GameListClose, IDC_GameListDoFilter },
\r
273 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
274 { DLG_Error, IDOK },
\r
275 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
276 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
277 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
278 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
279 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
280 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
281 { DLG_IndexNumber, IDC_Index },
\r
282 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
283 { DLG_TypeInName, IDOK, IDCANCEL },
\r
284 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
285 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
286 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
287 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
288 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
289 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
290 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
291 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
292 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
293 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
294 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
295 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
296 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
297 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
298 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
299 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
300 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
301 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
302 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
303 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
304 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
305 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
306 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
307 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
308 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
309 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
310 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
311 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
312 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
313 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
314 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
315 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
316 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
317 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
318 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
319 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
320 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
321 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
322 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
323 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
324 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
325 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
326 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
327 { DLG_MoveHistory },
\r
328 { DLG_EvalGraph },
\r
329 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
330 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
331 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
332 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
333 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
334 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
335 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
336 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
337 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
341 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
342 static int lastChecked;
\r
343 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
344 extern int tinyLayout;
\r
345 extern char * menuBarText[][10];
\r
348 LoadLanguageFile(char *name)
\r
349 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
351 int i=0, j=0, n=0, k;
\r
354 if(!name || name[0] == NULLCHAR) return;
\r
355 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
356 appData.language = oldLanguage;
\r
357 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
358 if((f = fopen(buf, "r")) == NULL) return;
\r
359 while((k = fgetc(f)) != EOF) {
\r
360 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
361 languageBuf[i] = k;
\r
363 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
365 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
366 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
367 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
368 english[j] = languageBuf + n + 1; *p = 0;
\r
369 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
370 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
375 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
377 case 'n': k = '\n'; break;
\r
378 case 'r': k = '\r'; break;
\r
379 case 't': k = '\t'; break;
\r
381 languageBuf[--i] = k;
\r
386 barbaric = (j != 0);
\r
387 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
392 { // return the translation of the given string
\r
393 // efficiency can be improved a lot...
\r
395 static char buf[MSG_SIZ];
\r
396 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
397 if(!barbaric) return s;
\r
398 if(!s) return ""; // sanity
\r
399 while(english[i]) {
\r
400 if(!strcmp(s, english[i])) return foreign[i];
\r
401 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
402 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
411 Translate(HWND hDlg, int dialogID)
\r
412 { // translate all text items in the given dialog
\r
414 char buf[MSG_SIZ], *s;
\r
415 if(!barbaric) return;
\r
416 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
417 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
418 GetWindowText( hDlg, buf, MSG_SIZ );
\r
420 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
421 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
422 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
423 if(strlen(buf) == 0) continue;
\r
425 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
430 TranslateOneMenu(int i, HMENU subMenu)
\r
433 static MENUITEMINFO info;
\r
435 info.cbSize = sizeof(MENUITEMINFO);
\r
436 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
437 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
439 info.dwTypeData = buf;
\r
440 info.cch = sizeof(buf);
\r
441 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
443 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
444 else menuText[i][j] = strdup(buf); // remember original on first change
\r
446 if(buf[0] == NULLCHAR) continue;
\r
447 info.dwTypeData = T_(buf);
\r
448 info.cch = strlen(buf)+1;
\r
449 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
455 TranslateMenus(int addLanguage)
\r
458 WIN32_FIND_DATA fileData;
\r
460 #define IDM_English 1970
\r
462 HMENU mainMenu = GetMenu(hwndMain);
\r
463 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
464 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
465 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
466 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
467 TranslateOneMenu(i, subMenu);
\r
469 DrawMenuBar(hwndMain);
\r
472 if(!addLanguage) return;
\r
473 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
474 HMENU mainMenu = GetMenu(hwndMain);
\r
475 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
476 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
477 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
478 i = 0; lastChecked = IDM_English;
\r
480 char *p, *q = fileData.cFileName;
\r
481 int checkFlag = MF_UNCHECKED;
\r
482 languageFile[i] = strdup(q);
\r
483 if(barbaric && !strcmp(oldLanguage, q)) {
\r
484 checkFlag = MF_CHECKED;
\r
485 lastChecked = IDM_English + i + 1;
\r
486 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
488 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
489 p = strstr(fileData.cFileName, ".lng");
\r
491 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
492 } while(FindNextFile(hFind, &fileData));
\r
499 #define IDM_RecentEngines 3000
\r
502 RecentEngineMenu (char *s)
\r
504 if(appData.icsActive) return;
\r
505 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
506 HMENU mainMenu = GetMenu(hwndMain);
\r
507 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
508 int i=IDM_RecentEngines;
\r
509 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
510 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
512 char *p = strchr(s, '\n');
\r
513 if(p == NULL) return; // malformed!
\r
515 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
529 int cliWidth, cliHeight;
\r
532 SizeInfo sizeInfo[] =
\r
534 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
535 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
536 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
537 { "petite", 33, 1, 1, 1, 0, 0 },
\r
538 { "slim", 37, 2, 1, 0, 0, 0 },
\r
539 { "small", 40, 2, 1, 0, 0, 0 },
\r
540 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
541 { "middling", 49, 2, 0, 0, 0, 0 },
\r
542 { "average", 54, 2, 0, 0, 0, 0 },
\r
543 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
544 { "medium", 64, 3, 0, 0, 0, 0 },
\r
545 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
546 { "large", 80, 3, 0, 0, 0, 0 },
\r
547 { "big", 87, 3, 0, 0, 0, 0 },
\r
548 { "huge", 95, 3, 0, 0, 0, 0 },
\r
549 { "giant", 108, 3, 0, 0, 0, 0 },
\r
550 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
551 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
552 { NULL, 0, 0, 0, 0, 0, 0 }
\r
555 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
556 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
558 { 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
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
578 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
587 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
588 #define N_BUTTONS 5
\r
590 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
592 {"<<", IDM_ToStart, NULL, NULL},
\r
593 {"<", IDM_Backward, NULL, NULL},
\r
594 {"P", IDM_Pause, NULL, NULL},
\r
595 {">", IDM_Forward, NULL, NULL},
\r
596 {">>", IDM_ToEnd, NULL, NULL},
\r
599 int tinyLayout = 0, smallLayout = 0;
\r
600 #define MENU_BAR_ITEMS 9
\r
601 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
602 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
603 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
607 MySound sounds[(int)NSoundClasses];
\r
608 MyTextAttribs textAttribs[(int)NColorClasses];
\r
610 MyColorizeAttribs colorizeAttribs[] = {
\r
611 { (COLORREF)0, 0, N_("Shout Text") },
\r
612 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
613 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
614 { (COLORREF)0, 0, N_("Channel Text") },
\r
615 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
616 { (COLORREF)0, 0, N_("Tell Text") },
\r
617 { (COLORREF)0, 0, N_("Challenge Text") },
\r
618 { (COLORREF)0, 0, N_("Request Text") },
\r
619 { (COLORREF)0, 0, N_("Seek Text") },
\r
620 { (COLORREF)0, 0, N_("Normal Text") },
\r
621 { (COLORREF)0, 0, N_("None") }
\r
626 static char *commentTitle;
\r
627 static char *commentText;
\r
628 static int commentIndex;
\r
629 static Boolean editComment = FALSE;
\r
632 char errorTitle[MSG_SIZ];
\r
633 char errorMessage[2*MSG_SIZ];
\r
634 HWND errorDialog = NULL;
\r
635 BOOLEAN moveErrorMessageUp = FALSE;
\r
636 BOOLEAN consoleEcho = TRUE;
\r
637 CHARFORMAT consoleCF;
\r
638 COLORREF consoleBackgroundColor;
\r
640 char *programVersion;
\r
646 typedef int CPKind;
\r
655 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
658 #define INPUT_SOURCE_BUF_SIZE 4096
\r
660 typedef struct _InputSource {
\r
667 char buf[INPUT_SOURCE_BUF_SIZE];
\r
671 InputCallback func;
\r
672 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
676 InputSource *consoleInputSource;
\r
681 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
682 VOID ConsoleCreate();
\r
684 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
685 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
686 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
687 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
689 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
690 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
691 void ParseIcsTextMenu(char *icsTextMenuString);
\r
692 VOID PopUpNameDialog(char firstchar);
\r
693 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
697 int GameListOptions();
\r
699 int dummy; // [HGM] for obsolete args
\r
701 HWND hwndMain = NULL; /* root window*/
\r
702 HWND hwndConsole = NULL;
\r
703 HWND commentDialog = NULL;
\r
704 HWND moveHistoryDialog = NULL;
\r
705 HWND evalGraphDialog = NULL;
\r
706 HWND engineOutputDialog = NULL;
\r
707 HWND gameListDialog = NULL;
\r
708 HWND editTagsDialog = NULL;
\r
710 int commentUp = FALSE;
\r
712 WindowPlacement wpMain;
\r
713 WindowPlacement wpConsole;
\r
714 WindowPlacement wpComment;
\r
715 WindowPlacement wpMoveHistory;
\r
716 WindowPlacement wpEvalGraph;
\r
717 WindowPlacement wpEngineOutput;
\r
718 WindowPlacement wpGameList;
\r
719 WindowPlacement wpTags;
\r
721 VOID EngineOptionsPopup(); // [HGM] settings
\r
723 VOID GothicPopUp(char *title, VariantClass variant);
\r
725 * Setting "frozen" should disable all user input other than deleting
\r
726 * the window. We do this while engines are initializing themselves.
\r
728 static int frozen = 0;
\r
729 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
735 if (frozen) return;
\r
737 hmenu = GetMenu(hwndMain);
\r
738 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
739 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
741 DrawMenuBar(hwndMain);
\r
744 /* Undo a FreezeUI */
\r
750 if (!frozen) return;
\r
752 hmenu = GetMenu(hwndMain);
\r
753 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
754 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
756 DrawMenuBar(hwndMain);
\r
759 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
761 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
767 #define JAWS_ALT_INTERCEPT
\r
768 #define JAWS_KBUP_NAVIGATION
\r
769 #define JAWS_KBDOWN_NAVIGATION
\r
770 #define JAWS_MENU_ITEMS
\r
771 #define JAWS_SILENCE
\r
772 #define JAWS_REPLAY
\r
774 #define JAWS_COPYRIGHT
\r
775 #define JAWS_DELETE(X) X
\r
776 #define SAYMACHINEMOVE()
\r
780 /*---------------------------------------------------------------------------*\
\r
784 \*---------------------------------------------------------------------------*/
\r
787 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
788 LPSTR lpCmdLine, int nCmdShow)
\r
791 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
792 // INITCOMMONCONTROLSEX ex;
\r
796 LoadLibrary("RICHED32.DLL");
\r
797 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
799 if (!InitApplication(hInstance)) {
\r
802 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
809 // InitCommonControlsEx(&ex);
\r
810 InitCommonControls();
\r
812 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
813 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
814 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
816 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
818 while (GetMessage(&msg, /* message structure */
\r
819 NULL, /* handle of window receiving the message */
\r
820 0, /* lowest message to examine */
\r
821 0)) /* highest message to examine */
\r
824 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
825 // [HGM] navigate: switch between all windows with tab
\r
826 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
827 int i, currentElement = 0;
\r
829 // first determine what element of the chain we come from (if any)
\r
830 if(appData.icsActive) {
\r
831 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
832 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
834 if(engineOutputDialog && EngineOutputIsUp()) {
\r
835 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
836 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
838 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
839 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
841 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
842 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
843 if(msg.hwnd == e1) currentElement = 2; else
\r
844 if(msg.hwnd == e2) currentElement = 3; else
\r
845 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
846 if(msg.hwnd == mh) currentElement = 4; else
\r
847 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
848 if(msg.hwnd == hText) currentElement = 5; else
\r
849 if(msg.hwnd == hInput) currentElement = 6; else
\r
850 for (i = 0; i < N_BUTTONS; i++) {
\r
851 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
854 // determine where to go to
\r
855 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
857 currentElement = (currentElement + direction) % 7;
\r
858 switch(currentElement) {
\r
860 h = hwndMain; break; // passing this case always makes the loop exit
\r
862 h = buttonDesc[0].hwnd; break; // could be NULL
\r
864 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
867 if(!EngineOutputIsUp()) continue;
\r
870 if(!MoveHistoryIsUp()) continue;
\r
872 // case 6: // input to eval graph does not seem to get here!
\r
873 // if(!EvalGraphIsUp()) continue;
\r
874 // h = evalGraphDialog; break;
\r
876 if(!appData.icsActive) continue;
\r
880 if(!appData.icsActive) continue;
\r
886 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
887 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
890 continue; // this message now has been processed
\r
894 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
895 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
896 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
897 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
898 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
899 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
900 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
901 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
902 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
903 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
904 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
905 for(i=0; i<MAX_CHAT; i++)
\r
906 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
909 if(done) continue; // [HGM] chat: end patch
\r
910 TranslateMessage(&msg); /* Translates virtual key codes */
\r
911 DispatchMessage(&msg); /* Dispatches message to window */
\r
916 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
919 /*---------------------------------------------------------------------------*\
\r
921 * Initialization functions
\r
923 \*---------------------------------------------------------------------------*/
\r
927 { // update user logo if necessary
\r
928 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
930 if(appData.autoLogo) {
\r
931 curName = UserName();
\r
932 if(strcmp(curName, oldUserName)) {
\r
933 GetCurrentDirectory(MSG_SIZ, dir);
\r
934 SetCurrentDirectory(installDir);
\r
935 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
936 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
937 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
938 if(userLogo == NULL)
\r
939 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
940 SetCurrentDirectory(dir); /* return to prev directory */
\r
946 InitApplication(HINSTANCE hInstance)
\r
950 /* Fill in window class structure with parameters that describe the */
\r
953 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
954 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
955 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
956 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
957 wc.hInstance = hInstance; /* Owner of this class */
\r
958 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
959 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
960 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
961 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
962 wc.lpszClassName = szAppName; /* Name to register as */
\r
964 /* Register the window class and return success/failure code. */
\r
965 if (!RegisterClass(&wc)) return FALSE;
\r
967 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
968 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
970 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
971 wc.hInstance = hInstance;
\r
972 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
973 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
974 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
975 wc.lpszMenuName = NULL;
\r
976 wc.lpszClassName = szConsoleName;
\r
978 if (!RegisterClass(&wc)) return FALSE;
\r
983 /* Set by InitInstance, used by EnsureOnScreen */
\r
984 int screenHeight, screenWidth;
\r
987 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
989 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
990 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
991 if (*x > screenWidth - 32) *x = 0;
\r
992 if (*y > screenHeight - 32) *y = 0;
\r
993 if (*x < minX) *x = minX;
\r
994 if (*y < minY) *y = minY;
\r
998 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1000 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1001 GetCurrentDirectory(MSG_SIZ, dir);
\r
1002 SetCurrentDirectory(installDir);
\r
1003 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1004 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1006 if (cps->programLogo == NULL && appData.debugMode) {
\r
1007 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1009 } else if(appData.autoLogo) {
\r
1010 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1011 char *opponent = "";
\r
1012 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1013 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1014 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1015 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1016 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1017 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1020 if(appData.directory[n] && appData.directory[n][0]) {
\r
1021 SetCurrentDirectory(appData.directory[n]);
\r
1022 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1025 SetCurrentDirectory(dir); /* return to prev directory */
\r
1031 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1032 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1034 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1035 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1036 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1037 liteBackTextureMode = appData.liteBackTextureMode;
\r
1039 if (liteBackTexture == NULL && appData.debugMode) {
\r
1040 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1044 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1045 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1046 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1047 darkBackTextureMode = appData.darkBackTextureMode;
\r
1049 if (darkBackTexture == NULL && appData.debugMode) {
\r
1050 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1056 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1058 HWND hwnd; /* Main window handle. */
\r
1060 WINDOWPLACEMENT wp;
\r
1063 hInst = hInstance; /* Store instance handle in our global variable */
\r
1064 programName = szAppName;
\r
1066 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1067 *filepart = NULLCHAR;
\r
1068 SetCurrentDirectory(installDir);
\r
1070 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1072 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1073 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1074 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1075 /* xboard, and older WinBoards, controlled the move sound with the
\r
1076 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1077 always turn the option on (so that the backend will call us),
\r
1078 then let the user turn the sound off by setting it to silence if
\r
1079 desired. To accommodate old winboard.ini files saved by old
\r
1080 versions of WinBoard, we also turn off the sound if the option
\r
1081 was initially set to false. [HGM] taken out of InitAppData */
\r
1082 if (!appData.ringBellAfterMoves) {
\r
1083 sounds[(int)SoundMove].name = strdup("");
\r
1084 appData.ringBellAfterMoves = TRUE;
\r
1086 if (appData.debugMode) {
\r
1087 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1088 setbuf(debugFP, NULL);
\r
1091 LoadLanguageFile(appData.language);
\r
1095 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1096 // InitEngineUCI( installDir, &second );
\r
1098 /* Create a main window for this application instance. */
\r
1099 hwnd = CreateWindow(szAppName, szTitle,
\r
1100 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1101 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1102 NULL, NULL, hInstance, NULL);
\r
1105 /* If window could not be created, return "failure" */
\r
1110 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1111 LoadLogo(&first, 0, FALSE);
\r
1112 LoadLogo(&second, 1, appData.icsActive);
\r
1116 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1117 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1118 iconCurrent = iconWhite;
\r
1119 InitDrawingColors();
\r
1120 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1121 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1122 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1123 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1124 /* Compute window size for each board size, and use the largest
\r
1125 size that fits on this screen as the default. */
\r
1126 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1127 if (boardSize == (BoardSize)-1 &&
\r
1128 winH <= screenHeight
\r
1129 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1130 && winW <= screenWidth) {
\r
1131 boardSize = (BoardSize)ibs;
\r
1135 InitDrawingSizes(boardSize, 0);
\r
1136 RecentEngineMenu(appData.recentEngineList);
\r
1138 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1140 /* [AS] Load textures if specified */
\r
1143 mysrandom( (unsigned) time(NULL) );
\r
1145 /* [AS] Restore layout */
\r
1146 if( wpMoveHistory.visible ) {
\r
1147 MoveHistoryPopUp();
\r
1150 if( wpEvalGraph.visible ) {
\r
1154 if( wpEngineOutput.visible ) {
\r
1155 EngineOutputPopUp();
\r
1158 /* Make the window visible; update its client area; and return "success" */
\r
1159 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1160 wp.length = sizeof(WINDOWPLACEMENT);
\r
1162 wp.showCmd = nCmdShow;
\r
1163 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1164 wp.rcNormalPosition.left = wpMain.x;
\r
1165 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1166 wp.rcNormalPosition.top = wpMain.y;
\r
1167 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1168 SetWindowPlacement(hwndMain, &wp);
\r
1170 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1172 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1173 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1175 if (hwndConsole) {
\r
1177 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1178 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1180 ShowWindow(hwndConsole, nCmdShow);
\r
1181 SetActiveWindow(hwndConsole);
\r
1183 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1184 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1193 HMENU hmenu = GetMenu(hwndMain);
\r
1195 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1196 MF_BYCOMMAND|((appData.icsActive &&
\r
1197 *appData.icsCommPort != NULLCHAR) ?
\r
1198 MF_ENABLED : MF_GRAYED));
\r
1199 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1200 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1201 MF_CHECKED : MF_UNCHECKED));
\r
1204 //---------------------------------------------------------------------------------------------------------
\r
1206 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1207 #define XBOARD FALSE
\r
1209 #define OPTCHAR "/"
\r
1210 #define SEPCHAR "="
\r
1211 #define TOPLEVEL 0
\r
1215 // front-end part of option handling
\r
1218 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1220 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1221 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1224 lf->lfEscapement = 0;
\r
1225 lf->lfOrientation = 0;
\r
1226 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1227 lf->lfItalic = mfp->italic;
\r
1228 lf->lfUnderline = mfp->underline;
\r
1229 lf->lfStrikeOut = mfp->strikeout;
\r
1230 lf->lfCharSet = mfp->charset;
\r
1231 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1232 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1233 lf->lfQuality = DEFAULT_QUALITY;
\r
1234 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1235 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1239 CreateFontInMF(MyFont *mf)
\r
1241 LFfromMFP(&mf->lf, &mf->mfp);
\r
1242 if (mf->hf) DeleteObject(mf->hf);
\r
1243 mf->hf = CreateFontIndirect(&mf->lf);
\r
1246 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1248 colorVariable[] = {
\r
1249 &whitePieceColor,
\r
1250 &blackPieceColor,
\r
1251 &lightSquareColor,
\r
1252 &darkSquareColor,
\r
1253 &highlightSquareColor,
\r
1254 &premoveHighlightColor,
\r
1256 &consoleBackgroundColor,
\r
1257 &appData.fontForeColorWhite,
\r
1258 &appData.fontBackColorWhite,
\r
1259 &appData.fontForeColorBlack,
\r
1260 &appData.fontBackColorBlack,
\r
1261 &appData.evalHistColorWhite,
\r
1262 &appData.evalHistColorBlack,
\r
1263 &appData.highlightArrowColor,
\r
1266 /* Command line font name parser. NULL name means do nothing.
\r
1267 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1268 For backward compatibility, syntax without the colon is also
\r
1269 accepted, but font names with digits in them won't work in that case.
\r
1272 ParseFontName(char *name, MyFontParams *mfp)
\r
1275 if (name == NULL) return;
\r
1277 q = strchr(p, ':');
\r
1279 if (q - p >= sizeof(mfp->faceName))
\r
1280 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1281 memcpy(mfp->faceName, p, q - p);
\r
1282 mfp->faceName[q - p] = NULLCHAR;
\r
1285 q = mfp->faceName;
\r
1287 while (*p && !isdigit(*p)) {
\r
1289 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1290 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1292 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1295 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1296 mfp->pointSize = (float) atof(p);
\r
1297 mfp->bold = (strchr(p, 'b') != NULL);
\r
1298 mfp->italic = (strchr(p, 'i') != NULL);
\r
1299 mfp->underline = (strchr(p, 'u') != NULL);
\r
1300 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1301 mfp->charset = DEFAULT_CHARSET;
\r
1302 q = strchr(p, 'c');
\r
1304 mfp->charset = (BYTE) atoi(q+1);
\r
1308 ParseFont(char *name, int number)
\r
1309 { // wrapper to shield back-end from 'font'
\r
1310 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1315 { // in WB we have a 2D array of fonts; this initializes their description
\r
1317 /* Point font array elements to structures and
\r
1318 parse default font names */
\r
1319 for (i=0; i<NUM_FONTS; i++) {
\r
1320 for (j=0; j<NUM_SIZES; j++) {
\r
1321 font[j][i] = &fontRec[j][i];
\r
1322 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1329 { // here we create the actual fonts from the selected descriptions
\r
1331 for (i=0; i<NUM_FONTS; i++) {
\r
1332 for (j=0; j<NUM_SIZES; j++) {
\r
1333 CreateFontInMF(font[j][i]);
\r
1337 /* Color name parser.
\r
1338 X version accepts X color names, but this one
\r
1339 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1341 ParseColorName(char *name)
\r
1343 int red, green, blue, count;
\r
1344 char buf[MSG_SIZ];
\r
1346 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1348 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1349 &red, &green, &blue);
\r
1352 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1353 DisplayError(buf, 0);
\r
1354 return RGB(0, 0, 0);
\r
1356 return PALETTERGB(red, green, blue);
\r
1360 ParseColor(int n, char *name)
\r
1361 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1362 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1366 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1368 char *e = argValue;
\r
1372 if (*e == 'b') eff |= CFE_BOLD;
\r
1373 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1374 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1375 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1376 else if (*e == '#' || isdigit(*e)) break;
\r
1380 *color = ParseColorName(e);
\r
1384 ParseTextAttribs(ColorClass cc, char *s)
\r
1385 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1386 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1387 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1391 ParseBoardSize(void *addr, char *name)
\r
1392 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1393 BoardSize bs = SizeTiny;
\r
1394 while (sizeInfo[bs].name != NULL) {
\r
1395 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1396 *(BoardSize *)addr = bs;
\r
1401 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1406 { // [HGM] import name from appData first
\r
1409 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1410 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1411 textAttribs[cc].sound.data = NULL;
\r
1412 MyLoadSound(&textAttribs[cc].sound);
\r
1414 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1415 textAttribs[cc].sound.name = strdup("");
\r
1416 textAttribs[cc].sound.data = NULL;
\r
1418 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1419 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1420 sounds[sc].data = NULL;
\r
1421 MyLoadSound(&sounds[sc]);
\r
1426 SetCommPortDefaults()
\r
1428 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1429 dcb.DCBlength = sizeof(DCB);
\r
1430 dcb.BaudRate = 9600;
\r
1431 dcb.fBinary = TRUE;
\r
1432 dcb.fParity = FALSE;
\r
1433 dcb.fOutxCtsFlow = FALSE;
\r
1434 dcb.fOutxDsrFlow = FALSE;
\r
1435 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1436 dcb.fDsrSensitivity = FALSE;
\r
1437 dcb.fTXContinueOnXoff = TRUE;
\r
1438 dcb.fOutX = FALSE;
\r
1440 dcb.fNull = FALSE;
\r
1441 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1442 dcb.fAbortOnError = FALSE;
\r
1444 dcb.Parity = SPACEPARITY;
\r
1445 dcb.StopBits = ONESTOPBIT;
\r
1448 // [HGM] args: these three cases taken out to stay in front-end
\r
1450 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1451 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1452 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1453 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1455 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1456 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1457 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1458 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1459 ad->argName, mfp->faceName, mfp->pointSize,
\r
1460 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1461 mfp->bold ? "b" : "",
\r
1462 mfp->italic ? "i" : "",
\r
1463 mfp->underline ? "u" : "",
\r
1464 mfp->strikeout ? "s" : "",
\r
1465 (int)mfp->charset);
\r
1471 { // [HGM] copy the names from the internal WB variables to appData
\r
1474 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1475 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1476 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1477 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1481 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1482 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1483 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1484 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1485 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1486 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1487 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1488 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1489 (ta->effects) ? " " : "",
\r
1490 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1494 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1495 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1496 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1497 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1498 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1502 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1503 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1504 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1508 ParseCommPortSettings(char *s)
\r
1509 { // wrapper to keep dcb from back-end
\r
1510 ParseCommSettings(s, &dcb);
\r
1515 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1516 GetActualPlacement(hwndMain, &wpMain);
\r
1517 GetActualPlacement(hwndConsole, &wpConsole);
\r
1518 GetActualPlacement(commentDialog, &wpComment);
\r
1519 GetActualPlacement(editTagsDialog, &wpTags);
\r
1520 GetActualPlacement(gameListDialog, &wpGameList);
\r
1521 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1522 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1523 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1527 PrintCommPortSettings(FILE *f, char *name)
\r
1528 { // wrapper to shield back-end from DCB
\r
1529 PrintCommSettings(f, name, &dcb);
\r
1533 MySearchPath(char *installDir, char *name, char *fullname)
\r
1535 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1536 if(name[0]== '%') {
\r
1537 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1538 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1539 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1540 *strchr(buf, '%') = 0;
\r
1541 strcat(fullname, getenv(buf));
\r
1542 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1544 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1545 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1546 return (int) strlen(fullname);
\r
1548 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1552 MyGetFullPathName(char *name, char *fullname)
\r
1555 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1560 { // [HGM] args: allows testing if main window is realized from back-end
\r
1561 return hwndMain != NULL;
\r
1565 PopUpStartupDialog()
\r
1569 LoadLanguageFile(appData.language);
\r
1570 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1571 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1572 FreeProcInstance(lpProc);
\r
1575 /*---------------------------------------------------------------------------*\
\r
1577 * GDI board drawing routines
\r
1579 \*---------------------------------------------------------------------------*/
\r
1581 /* [AS] Draw square using background texture */
\r
1582 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1587 return; /* Should never happen! */
\r
1590 SetGraphicsMode( dst, GM_ADVANCED );
\r
1597 /* X reflection */
\r
1602 x.eDx = (FLOAT) dw + dx - 1;
\r
1605 SetWorldTransform( dst, &x );
\r
1608 /* Y reflection */
\r
1614 x.eDy = (FLOAT) dh + dy - 1;
\r
1616 SetWorldTransform( dst, &x );
\r
1624 x.eDx = (FLOAT) dx;
\r
1625 x.eDy = (FLOAT) dy;
\r
1628 SetWorldTransform( dst, &x );
\r
1632 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1640 SetWorldTransform( dst, &x );
\r
1642 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1645 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1647 PM_WP = (int) WhitePawn,
\r
1648 PM_WN = (int) WhiteKnight,
\r
1649 PM_WB = (int) WhiteBishop,
\r
1650 PM_WR = (int) WhiteRook,
\r
1651 PM_WQ = (int) WhiteQueen,
\r
1652 PM_WF = (int) WhiteFerz,
\r
1653 PM_WW = (int) WhiteWazir,
\r
1654 PM_WE = (int) WhiteAlfil,
\r
1655 PM_WM = (int) WhiteMan,
\r
1656 PM_WO = (int) WhiteCannon,
\r
1657 PM_WU = (int) WhiteUnicorn,
\r
1658 PM_WH = (int) WhiteNightrider,
\r
1659 PM_WA = (int) WhiteAngel,
\r
1660 PM_WC = (int) WhiteMarshall,
\r
1661 PM_WAB = (int) WhiteCardinal,
\r
1662 PM_WD = (int) WhiteDragon,
\r
1663 PM_WL = (int) WhiteLance,
\r
1664 PM_WS = (int) WhiteCobra,
\r
1665 PM_WV = (int) WhiteFalcon,
\r
1666 PM_WSG = (int) WhiteSilver,
\r
1667 PM_WG = (int) WhiteGrasshopper,
\r
1668 PM_WK = (int) WhiteKing,
\r
1669 PM_BP = (int) BlackPawn,
\r
1670 PM_BN = (int) BlackKnight,
\r
1671 PM_BB = (int) BlackBishop,
\r
1672 PM_BR = (int) BlackRook,
\r
1673 PM_BQ = (int) BlackQueen,
\r
1674 PM_BF = (int) BlackFerz,
\r
1675 PM_BW = (int) BlackWazir,
\r
1676 PM_BE = (int) BlackAlfil,
\r
1677 PM_BM = (int) BlackMan,
\r
1678 PM_BO = (int) BlackCannon,
\r
1679 PM_BU = (int) BlackUnicorn,
\r
1680 PM_BH = (int) BlackNightrider,
\r
1681 PM_BA = (int) BlackAngel,
\r
1682 PM_BC = (int) BlackMarshall,
\r
1683 PM_BG = (int) BlackGrasshopper,
\r
1684 PM_BAB = (int) BlackCardinal,
\r
1685 PM_BD = (int) BlackDragon,
\r
1686 PM_BL = (int) BlackLance,
\r
1687 PM_BS = (int) BlackCobra,
\r
1688 PM_BV = (int) BlackFalcon,
\r
1689 PM_BSG = (int) BlackSilver,
\r
1690 PM_BK = (int) BlackKing
\r
1693 static HFONT hPieceFont = NULL;
\r
1694 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1695 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1696 static int fontBitmapSquareSize = 0;
\r
1697 static char pieceToFontChar[(int) EmptySquare] =
\r
1698 { 'p', 'n', 'b', 'r', 'q',
\r
1699 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1700 'k', 'o', 'm', 'v', 't', 'w',
\r
1701 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1704 extern BOOL SetCharTable( char *table, const char * map );
\r
1705 /* [HGM] moved to backend.c */
\r
1707 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1710 BYTE r1 = GetRValue( color );
\r
1711 BYTE g1 = GetGValue( color );
\r
1712 BYTE b1 = GetBValue( color );
\r
1718 /* Create a uniform background first */
\r
1719 hbrush = CreateSolidBrush( color );
\r
1720 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1721 FillRect( hdc, &rc, hbrush );
\r
1722 DeleteObject( hbrush );
\r
1725 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1726 int steps = squareSize / 2;
\r
1729 for( i=0; i<steps; i++ ) {
\r
1730 BYTE r = r1 - (r1-r2) * i / steps;
\r
1731 BYTE g = g1 - (g1-g2) * i / steps;
\r
1732 BYTE b = b1 - (b1-b2) * i / steps;
\r
1734 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1735 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1736 FillRect( hdc, &rc, hbrush );
\r
1737 DeleteObject(hbrush);
\r
1740 else if( mode == 2 ) {
\r
1741 /* Diagonal gradient, good more or less for every piece */
\r
1742 POINT triangle[3];
\r
1743 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1744 HBRUSH hbrush_old;
\r
1745 int steps = squareSize;
\r
1748 triangle[0].x = squareSize - steps;
\r
1749 triangle[0].y = squareSize;
\r
1750 triangle[1].x = squareSize;
\r
1751 triangle[1].y = squareSize;
\r
1752 triangle[2].x = squareSize;
\r
1753 triangle[2].y = squareSize - steps;
\r
1755 for( i=0; i<steps; i++ ) {
\r
1756 BYTE r = r1 - (r1-r2) * i / steps;
\r
1757 BYTE g = g1 - (g1-g2) * i / steps;
\r
1758 BYTE b = b1 - (b1-b2) * i / steps;
\r
1760 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1761 hbrush_old = SelectObject( hdc, hbrush );
\r
1762 Polygon( hdc, triangle, 3 );
\r
1763 SelectObject( hdc, hbrush_old );
\r
1764 DeleteObject(hbrush);
\r
1769 SelectObject( hdc, hpen );
\r
1774 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1775 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1776 piece: follow the steps as explained below.
\r
1778 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1782 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1786 int backColor = whitePieceColor;
\r
1787 int foreColor = blackPieceColor;
\r
1789 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1790 backColor = appData.fontBackColorWhite;
\r
1791 foreColor = appData.fontForeColorWhite;
\r
1793 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1794 backColor = appData.fontBackColorBlack;
\r
1795 foreColor = appData.fontForeColorBlack;
\r
1799 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1801 hbm_old = SelectObject( hdc, hbm );
\r
1805 rc.right = squareSize;
\r
1806 rc.bottom = squareSize;
\r
1808 /* Step 1: background is now black */
\r
1809 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1811 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1813 pt.x = (squareSize - sz.cx) / 2;
\r
1814 pt.y = (squareSize - sz.cy) / 2;
\r
1816 SetBkMode( hdc, TRANSPARENT );
\r
1817 SetTextColor( hdc, chroma );
\r
1818 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1819 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1821 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1822 /* Step 3: the area outside the piece is filled with white */
\r
1823 // FloodFill( hdc, 0, 0, chroma );
\r
1824 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1825 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1826 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1827 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1828 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1830 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1831 but if the start point is not inside the piece we're lost!
\r
1832 There should be a better way to do this... if we could create a region or path
\r
1833 from the fill operation we would be fine for example.
\r
1835 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1836 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1838 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1839 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1840 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1842 SelectObject( dc2, bm2 );
\r
1843 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1844 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1845 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1846 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1847 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1850 DeleteObject( bm2 );
\r
1853 SetTextColor( hdc, 0 );
\r
1855 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1856 draw the piece again in black for safety.
\r
1858 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1860 SelectObject( hdc, hbm_old );
\r
1862 if( hPieceMask[index] != NULL ) {
\r
1863 DeleteObject( hPieceMask[index] );
\r
1866 hPieceMask[index] = hbm;
\r
1869 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1871 SelectObject( hdc, hbm );
\r
1874 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1875 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1876 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1878 SelectObject( dc1, hPieceMask[index] );
\r
1879 SelectObject( dc2, bm2 );
\r
1880 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1881 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1884 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1885 the piece background and deletes (makes transparent) the rest.
\r
1886 Thanks to that mask, we are free to paint the background with the greates
\r
1887 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1888 We use this, to make gradients and give the pieces a "roundish" look.
\r
1890 SetPieceBackground( hdc, backColor, 2 );
\r
1891 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1895 DeleteObject( bm2 );
\r
1898 SetTextColor( hdc, foreColor );
\r
1899 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1901 SelectObject( hdc, hbm_old );
\r
1903 if( hPieceFace[index] != NULL ) {
\r
1904 DeleteObject( hPieceFace[index] );
\r
1907 hPieceFace[index] = hbm;
\r
1910 static int TranslatePieceToFontPiece( int piece )
\r
1940 case BlackMarshall:
\r
1944 case BlackNightrider:
\r
1950 case BlackUnicorn:
\r
1954 case BlackGrasshopper:
\r
1966 case BlackCardinal:
\r
1973 case WhiteMarshall:
\r
1977 case WhiteNightrider:
\r
1983 case WhiteUnicorn:
\r
1987 case WhiteGrasshopper:
\r
1999 case WhiteCardinal:
\r
2008 void CreatePiecesFromFont()
\r
2011 HDC hdc_window = NULL;
\r
2017 if( fontBitmapSquareSize < 0 ) {
\r
2018 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2022 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2023 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2024 fontBitmapSquareSize = -1;
\r
2028 if( fontBitmapSquareSize != squareSize ) {
\r
2029 hdc_window = GetDC( hwndMain );
\r
2030 hdc = CreateCompatibleDC( hdc_window );
\r
2032 if( hPieceFont != NULL ) {
\r
2033 DeleteObject( hPieceFont );
\r
2036 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2037 hPieceMask[i] = NULL;
\r
2038 hPieceFace[i] = NULL;
\r
2044 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2045 fontHeight = appData.fontPieceSize;
\r
2048 fontHeight = (fontHeight * squareSize) / 100;
\r
2050 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2052 lf.lfEscapement = 0;
\r
2053 lf.lfOrientation = 0;
\r
2054 lf.lfWeight = FW_NORMAL;
\r
2056 lf.lfUnderline = 0;
\r
2057 lf.lfStrikeOut = 0;
\r
2058 lf.lfCharSet = DEFAULT_CHARSET;
\r
2059 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2060 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2061 lf.lfQuality = PROOF_QUALITY;
\r
2062 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2063 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2064 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2066 hPieceFont = CreateFontIndirect( &lf );
\r
2068 if( hPieceFont == NULL ) {
\r
2069 fontBitmapSquareSize = -2;
\r
2072 /* Setup font-to-piece character table */
\r
2073 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2074 /* No (or wrong) global settings, try to detect the font */
\r
2075 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2077 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2079 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2080 /* DiagramTT* family */
\r
2081 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2083 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2084 /* Fairy symbols */
\r
2085 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2087 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2088 /* Good Companion (Some characters get warped as literal :-( */
\r
2089 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2090 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2091 SetCharTable(pieceToFontChar, s);
\r
2094 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2095 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2099 /* Create bitmaps */
\r
2100 hfont_old = SelectObject( hdc, hPieceFont );
\r
2101 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2102 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2103 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2105 SelectObject( hdc, hfont_old );
\r
2107 fontBitmapSquareSize = squareSize;
\r
2111 if( hdc != NULL ) {
\r
2115 if( hdc_window != NULL ) {
\r
2116 ReleaseDC( hwndMain, hdc_window );
\r
2121 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2123 char name[128], buf[MSG_SIZ];
\r
2125 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2126 if(appData.pieceDirectory[0]) {
\r
2128 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2129 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2130 if(res) return res;
\r
2132 if (gameInfo.event &&
\r
2133 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2134 strcmp(name, "k80s") == 0) {
\r
2135 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2137 return LoadBitmap(hinst, name);
\r
2141 /* Insert a color into the program's logical palette
\r
2142 structure. This code assumes the given color is
\r
2143 the result of the RGB or PALETTERGB macro, and it
\r
2144 knows how those macros work (which is documented).
\r
2147 InsertInPalette(COLORREF color)
\r
2149 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2151 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2152 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2153 pLogPal->palNumEntries--;
\r
2157 pe->peFlags = (char) 0;
\r
2158 pe->peRed = (char) (0xFF & color);
\r
2159 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2160 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2166 InitDrawingColors()
\r
2169 if (pLogPal == NULL) {
\r
2170 /* Allocate enough memory for a logical palette with
\r
2171 * PALETTESIZE entries and set the size and version fields
\r
2172 * of the logical palette structure.
\r
2174 pLogPal = (NPLOGPALETTE)
\r
2175 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2176 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2177 pLogPal->palVersion = 0x300;
\r
2179 pLogPal->palNumEntries = 0;
\r
2181 InsertInPalette(lightSquareColor);
\r
2182 InsertInPalette(darkSquareColor);
\r
2183 InsertInPalette(whitePieceColor);
\r
2184 InsertInPalette(blackPieceColor);
\r
2185 InsertInPalette(highlightSquareColor);
\r
2186 InsertInPalette(premoveHighlightColor);
\r
2188 /* create a logical color palette according the information
\r
2189 * in the LOGPALETTE structure.
\r
2191 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2193 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2194 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2195 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2196 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2197 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2198 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2199 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2200 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2202 /* [AS] Force rendering of the font-based pieces */
\r
2203 if( fontBitmapSquareSize > 0 ) {
\r
2204 fontBitmapSquareSize = 0;
\r
2210 BoardWidth(int boardSize, int n)
\r
2211 { /* [HGM] argument n added to allow different width and height */
\r
2212 int lineGap = sizeInfo[boardSize].lineGap;
\r
2214 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2215 lineGap = appData.overrideLineGap;
\r
2218 return (n + 1) * lineGap +
\r
2219 n * sizeInfo[boardSize].squareSize;
\r
2222 /* Respond to board resize by dragging edge */
\r
2224 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2226 BoardSize newSize = NUM_SIZES - 1;
\r
2227 static int recurse = 0;
\r
2228 if (IsIconic(hwndMain)) return;
\r
2229 if (recurse > 0) return;
\r
2231 while (newSize > 0) {
\r
2232 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2233 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2234 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2237 boardSize = newSize;
\r
2238 InitDrawingSizes(boardSize, flags);
\r
2243 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2246 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2248 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2249 ChessSquare piece;
\r
2250 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2252 SIZE clockSize, messageSize;
\r
2254 char buf[MSG_SIZ];
\r
2256 HMENU hmenu = GetMenu(hwndMain);
\r
2257 RECT crect, wrect, oldRect;
\r
2259 LOGBRUSH logbrush;
\r
2260 VariantClass v = gameInfo.variant;
\r
2262 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2263 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2265 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2266 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2267 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2268 oldBoardSize = boardSize;
\r
2270 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2271 { // correct board size to one where built-in pieces exist
\r
2272 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2273 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2274 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2275 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2276 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2277 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2278 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2279 boardSize = SizeMiddling;
\r
2282 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2284 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2285 oldRect.top = wpMain.y;
\r
2286 oldRect.right = wpMain.x + wpMain.width;
\r
2287 oldRect.bottom = wpMain.y + wpMain.height;
\r
2289 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2290 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2291 squareSize = sizeInfo[boardSize].squareSize;
\r
2292 lineGap = sizeInfo[boardSize].lineGap;
\r
2293 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2294 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2296 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2297 lineGap = appData.overrideLineGap;
\r
2300 if (tinyLayout != oldTinyLayout) {
\r
2301 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2303 style &= ~WS_SYSMENU;
\r
2304 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2305 "&Minimize\tCtrl+F4");
\r
2307 style |= WS_SYSMENU;
\r
2308 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2310 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2312 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2313 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2314 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2316 DrawMenuBar(hwndMain);
\r
2319 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2320 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2322 /* Get text area sizes */
\r
2323 hdc = GetDC(hwndMain);
\r
2324 if (appData.clockMode) {
\r
2325 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2327 snprintf(buf, MSG_SIZ, _("White"));
\r
2329 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2330 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2331 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2332 str = _("We only care about the height here");
\r
2333 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2334 SelectObject(hdc, oldFont);
\r
2335 ReleaseDC(hwndMain, hdc);
\r
2337 /* Compute where everything goes */
\r
2338 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2339 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2340 logoHeight = 2*clockSize.cy;
\r
2341 leftLogoRect.left = OUTER_MARGIN;
\r
2342 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2343 leftLogoRect.top = OUTER_MARGIN;
\r
2344 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2346 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2347 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2348 rightLogoRect.top = OUTER_MARGIN;
\r
2349 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2352 whiteRect.left = leftLogoRect.right;
\r
2353 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2354 whiteRect.top = OUTER_MARGIN;
\r
2355 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2357 blackRect.right = rightLogoRect.left;
\r
2358 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2359 blackRect.top = whiteRect.top;
\r
2360 blackRect.bottom = whiteRect.bottom;
\r
2362 whiteRect.left = OUTER_MARGIN;
\r
2363 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2364 whiteRect.top = OUTER_MARGIN;
\r
2365 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2367 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2368 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2369 blackRect.top = whiteRect.top;
\r
2370 blackRect.bottom = whiteRect.bottom;
\r
2372 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2375 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2376 if (appData.showButtonBar) {
\r
2377 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2378 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2380 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2382 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2383 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2385 boardRect.left = OUTER_MARGIN;
\r
2386 boardRect.right = boardRect.left + boardWidth;
\r
2387 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2388 boardRect.bottom = boardRect.top + boardHeight;
\r
2390 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2391 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2392 oldTinyLayout = tinyLayout;
\r
2393 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2394 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2395 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2396 winW *= 1 + twoBoards;
\r
2397 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2398 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2399 wpMain.height = winH; // without disturbing window attachments
\r
2400 GetWindowRect(hwndMain, &wrect);
\r
2401 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2402 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2404 // [HGM] placement: let attached windows follow size change.
\r
2405 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2406 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2407 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2408 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2409 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2411 /* compensate if menu bar wrapped */
\r
2412 GetClientRect(hwndMain, &crect);
\r
2413 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2414 wpMain.height += offby;
\r
2416 case WMSZ_TOPLEFT:
\r
2417 SetWindowPos(hwndMain, NULL,
\r
2418 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2419 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2422 case WMSZ_TOPRIGHT:
\r
2424 SetWindowPos(hwndMain, NULL,
\r
2425 wrect.left, wrect.bottom - wpMain.height,
\r
2426 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2429 case WMSZ_BOTTOMLEFT:
\r
2431 SetWindowPos(hwndMain, NULL,
\r
2432 wrect.right - wpMain.width, wrect.top,
\r
2433 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2436 case WMSZ_BOTTOMRIGHT:
\r
2440 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2441 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2446 for (i = 0; i < N_BUTTONS; i++) {
\r
2447 if (buttonDesc[i].hwnd != NULL) {
\r
2448 DestroyWindow(buttonDesc[i].hwnd);
\r
2449 buttonDesc[i].hwnd = NULL;
\r
2451 if (appData.showButtonBar) {
\r
2452 buttonDesc[i].hwnd =
\r
2453 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2454 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2455 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2456 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2457 (HMENU) buttonDesc[i].id,
\r
2458 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2460 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2461 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2462 MAKELPARAM(FALSE, 0));
\r
2464 if (buttonDesc[i].id == IDM_Pause)
\r
2465 hwndPause = buttonDesc[i].hwnd;
\r
2466 buttonDesc[i].wndproc = (WNDPROC)
\r
2467 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2470 if (gridPen != NULL) DeleteObject(gridPen);
\r
2471 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2472 if (premovePen != NULL) DeleteObject(premovePen);
\r
2473 if (lineGap != 0) {
\r
2474 logbrush.lbStyle = BS_SOLID;
\r
2475 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2477 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2478 lineGap, &logbrush, 0, NULL);
\r
2479 logbrush.lbColor = highlightSquareColor;
\r
2481 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2482 lineGap, &logbrush, 0, NULL);
\r
2484 logbrush.lbColor = premoveHighlightColor;
\r
2486 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2487 lineGap, &logbrush, 0, NULL);
\r
2489 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2490 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2491 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2492 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2493 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2494 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2495 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2496 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2498 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2499 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2500 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2501 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2502 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2503 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2504 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2505 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2509 /* [HGM] Licensing requirement */
\r
2511 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2514 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2516 GothicPopUp( "", VariantNormal);
\r
2519 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2521 /* Load piece bitmaps for this board size */
\r
2522 for (i=0; i<=2; i++) {
\r
2523 for (piece = WhitePawn;
\r
2524 (int) piece < (int) BlackPawn;
\r
2525 piece = (ChessSquare) ((int) piece + 1)) {
\r
2526 if (pieceBitmap[i][piece] != NULL)
\r
2527 DeleteObject(pieceBitmap[i][piece]);
\r
2531 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2532 // Orthodox Chess pieces
\r
2533 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2534 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2535 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2536 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2537 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2538 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2539 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2540 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2541 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2542 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2543 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2544 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2545 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2546 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2547 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2548 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2549 // in Shogi, Hijack the unused Queen for Lance
\r
2550 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2559 if(squareSize <= 72 && squareSize >= 33) {
\r
2560 /* A & C are available in most sizes now */
\r
2561 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2562 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2574 } else { // Smirf-like
\r
2575 if(gameInfo.variant == VariantSChess) {
\r
2576 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2585 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2586 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2589 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2590 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2593 } else { // WinBoard standard
\r
2594 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2601 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2602 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2603 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2604 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2605 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2606 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2607 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2608 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2609 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2610 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2611 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2614 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2617 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2620 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2623 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2624 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2625 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2626 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2627 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2628 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2629 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2630 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2631 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2633 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2634 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2635 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2636 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2637 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2638 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2639 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2640 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2641 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2642 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2643 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2644 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2645 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2653 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2654 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2655 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2656 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2657 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2658 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2661 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2662 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2663 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2664 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2665 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2666 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2667 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2668 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2669 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2670 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2671 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2672 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2673 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2674 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2675 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2679 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2680 /* special Shogi support in this size */
\r
2681 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2682 for (piece = WhitePawn;
\r
2683 (int) piece < (int) BlackPawn;
\r
2684 piece = (ChessSquare) ((int) piece + 1)) {
\r
2685 if (pieceBitmap[i][piece] != NULL)
\r
2686 DeleteObject(pieceBitmap[i][piece]);
\r
2689 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2690 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2691 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2693 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2696 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2697 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2698 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2699 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2700 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2701 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2702 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2703 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2704 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2705 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2707 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2710 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2711 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2712 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2713 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2714 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2715 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2716 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2717 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2718 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2719 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2721 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2724 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2725 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2726 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2727 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2728 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2729 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2730 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2736 PieceBitmap(ChessSquare p, int kind)
\r
2738 if ((int) p >= (int) BlackPawn)
\r
2739 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2741 return pieceBitmap[kind][(int) p];
\r
2744 /***************************************************************/
\r
2746 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2747 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2749 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2750 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2754 SquareToPos(int row, int column, int * x, int * y)
\r
2757 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2758 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2760 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2761 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2766 DrawCoordsOnDC(HDC hdc)
\r
2768 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2769 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2770 char str[2] = { NULLCHAR, NULLCHAR };
\r
2771 int oldMode, oldAlign, x, y, start, i;
\r
2775 if (!appData.showCoords)
\r
2778 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2780 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2781 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2782 oldAlign = GetTextAlign(hdc);
\r
2783 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2785 y = boardRect.top + lineGap;
\r
2786 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2789 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2790 x += border - lineGap - 4; y += squareSize - 6;
\r
2792 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2793 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2794 str[0] = files[start + i];
\r
2795 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2796 y += squareSize + lineGap;
\r
2799 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2802 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2803 x += -border + 4; y += border - squareSize + 6;
\r
2805 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2806 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2807 str[0] = ranks[start + i];
\r
2808 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2809 x += squareSize + lineGap;
\r
2812 SelectObject(hdc, oldBrush);
\r
2813 SetBkMode(hdc, oldMode);
\r
2814 SetTextAlign(hdc, oldAlign);
\r
2815 SelectObject(hdc, oldFont);
\r
2819 DrawGridOnDC(HDC hdc)
\r
2823 if (lineGap != 0) {
\r
2824 oldPen = SelectObject(hdc, gridPen);
\r
2825 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2826 SelectObject(hdc, oldPen);
\r
2830 #define HIGHLIGHT_PEN 0
\r
2831 #define PREMOVE_PEN 1
\r
2834 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2837 HPEN oldPen, hPen;
\r
2838 if (lineGap == 0) return;
\r
2840 x1 = boardRect.left +
\r
2841 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2842 y1 = boardRect.top +
\r
2843 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2845 x1 = boardRect.left +
\r
2846 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2847 y1 = boardRect.top +
\r
2848 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2850 hPen = pen ? premovePen : highlightPen;
\r
2851 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2852 MoveToEx(hdc, x1, y1, NULL);
\r
2853 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2854 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2855 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2856 LineTo(hdc, x1, y1);
\r
2857 SelectObject(hdc, oldPen);
\r
2861 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2864 for (i=0; i<2; i++) {
\r
2865 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2866 DrawHighlightOnDC(hdc, TRUE,
\r
2867 h->sq[i].x, h->sq[i].y,
\r
2872 /* Note: sqcolor is used only in monoMode */
\r
2873 /* Note that this code is largely duplicated in woptions.c,
\r
2874 function DrawSampleSquare, so that needs to be updated too */
\r
2876 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2878 HBITMAP oldBitmap;
\r
2882 if (appData.blindfold) return;
\r
2884 /* [AS] Use font-based pieces if needed */
\r
2885 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2886 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2887 CreatePiecesFromFont();
\r
2889 if( fontBitmapSquareSize == squareSize ) {
\r
2890 int index = TranslatePieceToFontPiece(piece);
\r
2892 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2894 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2895 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2899 squareSize, squareSize,
\r
2904 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2906 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2907 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2911 squareSize, squareSize,
\r
2920 if (appData.monoMode) {
\r
2921 SelectObject(tmphdc, PieceBitmap(piece,
\r
2922 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2923 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2924 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2926 HBRUSH xBrush = whitePieceBrush;
\r
2927 tmpSize = squareSize;
\r
2928 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2930 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2931 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2932 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2933 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2934 x += (squareSize - minorSize)>>1;
\r
2935 y += squareSize - minorSize - 2;
\r
2936 tmpSize = minorSize;
\r
2938 if (color || appData.allWhite ) {
\r
2939 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2941 oldBrush = SelectObject(hdc, xBrush);
\r
2942 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2943 if(appData.upsideDown && color==flipView)
\r
2944 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2946 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2947 /* Use black for outline of white pieces */
\r
2948 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2949 if(appData.upsideDown && color==flipView)
\r
2950 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2952 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2953 } else if(appData.pieceDirectory[0]) {
\r
2954 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2955 oldBrush = SelectObject(hdc, xBrush);
\r
2956 if(appData.upsideDown && color==flipView)
\r
2957 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2959 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2960 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2961 if(appData.upsideDown && color==flipView)
\r
2962 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2964 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2966 /* Use square color for details of black pieces */
\r
2967 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2968 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2969 if(appData.upsideDown && !flipView)
\r
2970 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2972 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2974 SelectObject(hdc, oldBrush);
\r
2975 SelectObject(tmphdc, oldBitmap);
\r
2979 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2980 int GetBackTextureMode( int algo )
\r
2982 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2986 case BACK_TEXTURE_MODE_PLAIN:
\r
2987 result = 1; /* Always use identity map */
\r
2989 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2990 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2998 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2999 to handle redraws cleanly (as random numbers would always be different).
\r
3001 VOID RebuildTextureSquareInfo()
\r
3011 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3013 if( liteBackTexture != NULL ) {
\r
3014 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3015 lite_w = bi.bmWidth;
\r
3016 lite_h = bi.bmHeight;
\r
3020 if( darkBackTexture != NULL ) {
\r
3021 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3022 dark_w = bi.bmWidth;
\r
3023 dark_h = bi.bmHeight;
\r
3027 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3028 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3029 if( (col + row) & 1 ) {
\r
3031 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3032 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3033 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3035 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3036 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3037 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3039 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3040 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3045 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3046 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3047 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3049 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3050 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3051 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3053 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3054 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3061 /* [AS] Arrow highlighting support */
\r
3063 static double A_WIDTH = 5; /* Width of arrow body */
\r
3065 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3066 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3068 static double Sqr( double x )
\r
3073 static int Round( double x )
\r
3075 return (int) (x + 0.5);
\r
3078 /* Draw an arrow between two points using current settings */
\r
3079 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3082 double dx, dy, j, k, x, y;
\r
3084 if( d_x == s_x ) {
\r
3085 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3087 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3090 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3091 arrow[1].y = d_y - h;
\r
3093 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3094 arrow[2].y = d_y - h;
\r
3099 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3100 arrow[5].y = d_y - h;
\r
3102 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3103 arrow[4].y = d_y - h;
\r
3105 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3108 else if( d_y == s_y ) {
\r
3109 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3112 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3114 arrow[1].x = d_x - w;
\r
3115 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3117 arrow[2].x = d_x - w;
\r
3118 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3123 arrow[5].x = d_x - w;
\r
3124 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3126 arrow[4].x = d_x - w;
\r
3127 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3130 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3133 /* [AS] Needed a lot of paper for this! :-) */
\r
3134 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3135 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3137 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3139 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3144 arrow[0].x = Round(x - j);
\r
3145 arrow[0].y = Round(y + j*dx);
\r
3147 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3148 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3151 x = (double) d_x - k;
\r
3152 y = (double) d_y - k*dy;
\r
3155 x = (double) d_x + k;
\r
3156 y = (double) d_y + k*dy;
\r
3159 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3161 arrow[6].x = Round(x - j);
\r
3162 arrow[6].y = Round(y + j*dx);
\r
3164 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3165 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3167 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3168 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3173 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3174 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3177 Polygon( hdc, arrow, 7 );
\r
3180 /* [AS] Draw an arrow between two squares */
\r
3181 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3183 int s_x, s_y, d_x, d_y;
\r
3190 if( s_col == d_col && s_row == d_row ) {
\r
3194 /* Get source and destination points */
\r
3195 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3196 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3199 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3201 else if( d_y < s_y ) {
\r
3202 d_y += squareSize / 2 + squareSize / 4;
\r
3205 d_y += squareSize / 2;
\r
3209 d_x += squareSize / 2 - squareSize / 4;
\r
3211 else if( d_x < s_x ) {
\r
3212 d_x += squareSize / 2 + squareSize / 4;
\r
3215 d_x += squareSize / 2;
\r
3218 s_x += squareSize / 2;
\r
3219 s_y += squareSize / 2;
\r
3221 /* Adjust width */
\r
3222 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3225 stLB.lbStyle = BS_SOLID;
\r
3226 stLB.lbColor = appData.highlightArrowColor;
\r
3229 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3230 holdpen = SelectObject( hdc, hpen );
\r
3231 hbrush = CreateBrushIndirect( &stLB );
\r
3232 holdbrush = SelectObject( hdc, hbrush );
\r
3234 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3236 SelectObject( hdc, holdpen );
\r
3237 SelectObject( hdc, holdbrush );
\r
3238 DeleteObject( hpen );
\r
3239 DeleteObject( hbrush );
\r
3242 BOOL HasHighlightInfo()
\r
3244 BOOL result = FALSE;
\r
3246 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3247 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3255 BOOL IsDrawArrowEnabled()
\r
3257 BOOL result = FALSE;
\r
3259 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3266 VOID DrawArrowHighlight( HDC hdc )
\r
3268 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3269 DrawArrowBetweenSquares( hdc,
\r
3270 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3271 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3275 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3277 HRGN result = NULL;
\r
3279 if( HasHighlightInfo() ) {
\r
3280 int x1, y1, x2, y2;
\r
3281 int sx, sy, dx, dy;
\r
3283 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3284 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3286 sx = MIN( x1, x2 );
\r
3287 sy = MIN( y1, y2 );
\r
3288 dx = MAX( x1, x2 ) + squareSize;
\r
3289 dy = MAX( y1, y2 ) + squareSize;
\r
3291 result = CreateRectRgn( sx, sy, dx, dy );
\r
3298 Warning: this function modifies the behavior of several other functions.
\r
3300 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3301 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3302 repaint is scattered all over the place, which is not good for features such as
\r
3303 "arrow highlighting" that require a full repaint of the board.
\r
3305 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3306 user interaction, when speed is not so important) but especially to avoid errors
\r
3307 in the displayed graphics.
\r
3309 In such patched places, I always try refer to this function so there is a single
\r
3310 place to maintain knowledge.
\r
3312 To restore the original behavior, just return FALSE unconditionally.
\r
3314 BOOL IsFullRepaintPreferrable()
\r
3316 BOOL result = FALSE;
\r
3318 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3319 /* Arrow may appear on the board */
\r
3327 This function is called by DrawPosition to know whether a full repaint must
\r
3330 Only DrawPosition may directly call this function, which makes use of
\r
3331 some state information. Other function should call DrawPosition specifying
\r
3332 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3334 BOOL DrawPositionNeedsFullRepaint()
\r
3336 BOOL result = FALSE;
\r
3339 Probably a slightly better policy would be to trigger a full repaint
\r
3340 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3341 but animation is fast enough that it's difficult to notice.
\r
3343 if( animInfo.piece == EmptySquare ) {
\r
3344 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3352 static HBITMAP borderBitmap;
\r
3355 DrawBackgroundOnDC(HDC hdc)
\r
3361 static char oldBorder[MSG_SIZ];
\r
3362 int w = 600, h = 600, mode;
\r
3364 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3365 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3366 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3368 if(borderBitmap == NULL) { // loading failed, use white
\r
3369 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3372 tmphdc = CreateCompatibleDC(hdc);
\r
3373 hbm = SelectObject(tmphdc, borderBitmap);
\r
3374 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3378 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3379 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3380 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3381 SetStretchBltMode(hdc, mode);
\r
3382 SelectObject(tmphdc, hbm);
\r
3387 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3389 int row, column, x, y, square_color, piece_color;
\r
3390 ChessSquare piece;
\r
3392 HDC texture_hdc = NULL;
\r
3394 /* [AS] Initialize background textures if needed */
\r
3395 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3396 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3397 if( backTextureSquareSize != squareSize
\r
3398 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3399 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3400 backTextureSquareSize = squareSize;
\r
3401 RebuildTextureSquareInfo();
\r
3404 texture_hdc = CreateCompatibleDC( hdc );
\r
3407 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3408 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3410 SquareToPos(row, column, &x, &y);
\r
3412 piece = board[row][column];
\r
3414 square_color = ((column + row) % 2) == 1;
\r
3415 if( gameInfo.variant == VariantXiangqi ) {
\r
3416 square_color = !InPalace(row, column);
\r
3417 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3418 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3420 piece_color = (int) piece < (int) BlackPawn;
\r
3423 /* [HGM] holdings file: light square or black */
\r
3424 if(column == BOARD_LEFT-2) {
\r
3425 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3428 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3432 if(column == BOARD_RGHT + 1 ) {
\r
3433 if( row < gameInfo.holdingsSize )
\r
3436 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3440 if(column == BOARD_LEFT-1 ) /* left align */
\r
3441 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3442 else if( column == BOARD_RGHT) /* right align */
\r
3443 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3445 if (appData.monoMode) {
\r
3446 if (piece == EmptySquare) {
\r
3447 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3448 square_color ? WHITENESS : BLACKNESS);
\r
3450 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3453 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3454 /* [AS] Draw the square using a texture bitmap */
\r
3455 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3456 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3457 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3460 squareSize, squareSize,
\r
3463 backTextureSquareInfo[r][c].mode,
\r
3464 backTextureSquareInfo[r][c].x,
\r
3465 backTextureSquareInfo[r][c].y );
\r
3467 SelectObject( texture_hdc, hbm );
\r
3469 if (piece != EmptySquare) {
\r
3470 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3474 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3476 oldBrush = SelectObject(hdc, brush );
\r
3477 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3478 SelectObject(hdc, oldBrush);
\r
3479 if (piece != EmptySquare)
\r
3480 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3485 if( texture_hdc != NULL ) {
\r
3486 DeleteDC( texture_hdc );
\r
3490 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3491 void fputDW(FILE *f, int x)
\r
3493 fputc(x & 255, f);
\r
3494 fputc(x>>8 & 255, f);
\r
3495 fputc(x>>16 & 255, f);
\r
3496 fputc(x>>24 & 255, f);
\r
3499 #define MAX_CLIPS 200 /* more than enough */
\r
3502 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3504 // HBITMAP bufferBitmap;
\r
3509 int w = 100, h = 50;
\r
3511 if(logo == NULL) {
\r
3512 if(!logoHeight) return;
\r
3513 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3515 // GetClientRect(hwndMain, &Rect);
\r
3516 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3517 // Rect.bottom-Rect.top+1);
\r
3518 tmphdc = CreateCompatibleDC(hdc);
\r
3519 hbm = SelectObject(tmphdc, logo);
\r
3520 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3524 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3525 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3526 SelectObject(tmphdc, hbm);
\r
3534 HDC hdc = GetDC(hwndMain);
\r
3535 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3536 if(appData.autoLogo) {
\r
3538 switch(gameMode) { // pick logos based on game mode
\r
3539 case IcsObserving:
\r
3540 whiteLogo = second.programLogo; // ICS logo
\r
3541 blackLogo = second.programLogo;
\r
3544 case IcsPlayingWhite:
\r
3545 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3546 blackLogo = second.programLogo; // ICS logo
\r
3548 case IcsPlayingBlack:
\r
3549 whiteLogo = second.programLogo; // ICS logo
\r
3550 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3552 case TwoMachinesPlay:
\r
3553 if(first.twoMachinesColor[0] == 'b') {
\r
3554 whiteLogo = second.programLogo;
\r
3555 blackLogo = first.programLogo;
\r
3558 case MachinePlaysWhite:
\r
3559 blackLogo = userLogo;
\r
3561 case MachinePlaysBlack:
\r
3562 whiteLogo = userLogo;
\r
3563 blackLogo = first.programLogo;
\r
3566 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3567 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3568 ReleaseDC(hwndMain, hdc);
\r
3573 UpdateLogos(int display)
\r
3574 { // called after loading new engine(s), in tourney or from menu
\r
3575 LoadLogo(&first, 0, FALSE);
\r
3576 LoadLogo(&second, 1, appData.icsActive);
\r
3577 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3578 if(display) DisplayLogos();
\r
3581 static HDC hdcSeek;
\r
3583 // [HGM] seekgraph
\r
3584 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3587 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3588 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3589 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3590 SelectObject( hdcSeek, hp );
\r
3593 // front-end wrapper for drawing functions to do rectangles
\r
3594 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3599 if (hdcSeek == NULL) {
\r
3600 hdcSeek = GetDC(hwndMain);
\r
3601 if (!appData.monoMode) {
\r
3602 SelectPalette(hdcSeek, hPal, FALSE);
\r
3603 RealizePalette(hdcSeek);
\r
3606 hp = SelectObject( hdcSeek, gridPen );
\r
3607 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3608 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3609 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3610 SelectObject( hdcSeek, hp );
\r
3613 // front-end wrapper for putting text in graph
\r
3614 void DrawSeekText(char *buf, int x, int y)
\r
3617 SetBkMode( hdcSeek, TRANSPARENT );
\r
3618 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3619 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3622 void DrawSeekDot(int x, int y, int color)
\r
3624 int square = color & 0x80;
\r
3625 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3626 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3629 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3630 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3632 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3633 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3634 SelectObject(hdcSeek, oldBrush);
\r
3637 void DrawSeekOpen()
\r
3641 void DrawSeekClose()
\r
3646 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3648 static Board lastReq[2], lastDrawn[2];
\r
3649 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3650 static int lastDrawnFlipView = 0;
\r
3651 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3652 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3655 HBITMAP bufferBitmap;
\r
3656 HBITMAP oldBitmap;
\r
3658 HRGN clips[MAX_CLIPS];
\r
3659 ChessSquare dragged_piece = EmptySquare;
\r
3660 int nr = twoBoards*partnerUp;
\r
3662 /* I'm undecided on this - this function figures out whether a full
\r
3663 * repaint is necessary on its own, so there's no real reason to have the
\r
3664 * caller tell it that. I think this can safely be set to FALSE - but
\r
3665 * if we trust the callers not to request full repaints unnessesarily, then
\r
3666 * we could skip some clipping work. In other words, only request a full
\r
3667 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3668 * gamestart and similar) --Hawk
\r
3670 Boolean fullrepaint = repaint;
\r
3672 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3674 if( DrawPositionNeedsFullRepaint() ) {
\r
3675 fullrepaint = TRUE;
\r
3678 if (board == NULL) {
\r
3679 if (!lastReqValid[nr]) {
\r
3682 board = lastReq[nr];
\r
3684 CopyBoard(lastReq[nr], board);
\r
3685 lastReqValid[nr] = 1;
\r
3688 if (doingSizing) {
\r
3692 if (IsIconic(hwndMain)) {
\r
3696 if (hdc == NULL) {
\r
3697 hdc = GetDC(hwndMain);
\r
3698 if (!appData.monoMode) {
\r
3699 SelectPalette(hdc, hPal, FALSE);
\r
3700 RealizePalette(hdc);
\r
3704 releaseDC = FALSE;
\r
3707 /* Create some work-DCs */
\r
3708 hdcmem = CreateCompatibleDC(hdc);
\r
3709 tmphdc = CreateCompatibleDC(hdc);
\r
3711 /* If dragging is in progress, we temporarely remove the piece */
\r
3712 /* [HGM] or temporarily decrease count if stacked */
\r
3713 /* !! Moved to before board compare !! */
\r
3714 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3715 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3716 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3717 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3718 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3720 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3721 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3722 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3724 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3727 /* Figure out which squares need updating by comparing the
\r
3728 * newest board with the last drawn board and checking if
\r
3729 * flipping has changed.
\r
3731 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3732 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3733 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3734 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3735 SquareToPos(row, column, &x, &y);
\r
3736 clips[num_clips++] =
\r
3737 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3741 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3742 for (i=0; i<2; i++) {
\r
3743 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3744 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3745 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3746 lastDrawnHighlight.sq[i].y >= 0) {
\r
3747 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3748 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3749 clips[num_clips++] =
\r
3750 CreateRectRgn(x - lineGap, y - lineGap,
\r
3751 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3753 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3754 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3755 clips[num_clips++] =
\r
3756 CreateRectRgn(x - lineGap, y - lineGap,
\r
3757 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3761 for (i=0; i<2; i++) {
\r
3762 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3763 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3764 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3765 lastDrawnPremove.sq[i].y >= 0) {
\r
3766 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3767 lastDrawnPremove.sq[i].x, &x, &y);
\r
3768 clips[num_clips++] =
\r
3769 CreateRectRgn(x - lineGap, y - lineGap,
\r
3770 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3772 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3773 premoveHighlightInfo.sq[i].y >= 0) {
\r
3774 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3775 premoveHighlightInfo.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
3782 } else { // nr == 1
\r
3783 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3784 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3785 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3786 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3787 for (i=0; i<2; i++) {
\r
3788 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3789 partnerHighlightInfo.sq[i].y >= 0) {
\r
3790 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3791 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3792 clips[num_clips++] =
\r
3793 CreateRectRgn(x - lineGap, y - lineGap,
\r
3794 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3796 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3797 oldPartnerHighlight.sq[i].y >= 0) {
\r
3798 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3799 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3800 clips[num_clips++] =
\r
3801 CreateRectRgn(x - lineGap, y - lineGap,
\r
3802 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3807 fullrepaint = TRUE;
\r
3810 /* Create a buffer bitmap - this is the actual bitmap
\r
3811 * being written to. When all the work is done, we can
\r
3812 * copy it to the real DC (the screen). This avoids
\r
3813 * the problems with flickering.
\r
3815 GetClientRect(hwndMain, &Rect);
\r
3816 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3817 Rect.bottom-Rect.top+1);
\r
3818 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3819 if (!appData.monoMode) {
\r
3820 SelectPalette(hdcmem, hPal, FALSE);
\r
3823 /* Create clips for dragging */
\r
3824 if (!fullrepaint) {
\r
3825 if (dragInfo.from.x >= 0) {
\r
3826 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3827 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3829 if (dragInfo.start.x >= 0) {
\r
3830 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3831 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3833 if (dragInfo.pos.x >= 0) {
\r
3834 x = dragInfo.pos.x - squareSize / 2;
\r
3835 y = dragInfo.pos.y - squareSize / 2;
\r
3836 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3838 if (dragInfo.lastpos.x >= 0) {
\r
3839 x = dragInfo.lastpos.x - squareSize / 2;
\r
3840 y = dragInfo.lastpos.y - squareSize / 2;
\r
3841 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3845 /* Are we animating a move?
\r
3847 * - remove the piece from the board (temporarely)
\r
3848 * - calculate the clipping region
\r
3850 if (!fullrepaint) {
\r
3851 if (animInfo.piece != EmptySquare) {
\r
3852 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3853 x = boardRect.left + animInfo.lastpos.x;
\r
3854 y = boardRect.top + animInfo.lastpos.y;
\r
3855 x2 = boardRect.left + animInfo.pos.x;
\r
3856 y2 = boardRect.top + animInfo.pos.y;
\r
3857 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3858 /* Slight kludge. The real problem is that after AnimateMove is
\r
3859 done, the position on the screen does not match lastDrawn.
\r
3860 This currently causes trouble only on e.p. captures in
\r
3861 atomic, where the piece moves to an empty square and then
\r
3862 explodes. The old and new positions both had an empty square
\r
3863 at the destination, but animation has drawn a piece there and
\r
3864 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3865 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3869 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3870 if (num_clips == 0)
\r
3871 fullrepaint = TRUE;
\r
3873 /* Set clipping on the memory DC */
\r
3874 if (!fullrepaint) {
\r
3875 SelectClipRgn(hdcmem, clips[0]);
\r
3876 for (x = 1; x < num_clips; x++) {
\r
3877 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3878 abort(); // this should never ever happen!
\r
3882 /* Do all the drawing to the memory DC */
\r
3883 if(explodeInfo.radius) { // [HGM] atomic
\r
3885 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3886 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3887 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3888 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3889 x += squareSize/2;
\r
3890 y += squareSize/2;
\r
3891 if(!fullrepaint) {
\r
3892 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3893 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3895 DrawGridOnDC(hdcmem);
\r
3896 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3897 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3898 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3899 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3900 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3901 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3902 SelectObject(hdcmem, oldBrush);
\r
3904 if(border) DrawBackgroundOnDC(hdcmem);
\r
3905 DrawGridOnDC(hdcmem);
\r
3906 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3907 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3908 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3910 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3911 oldPartnerHighlight = partnerHighlightInfo;
\r
3913 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3915 if(nr == 0) // [HGM] dual: markers only on left board
\r
3916 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3917 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3918 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3919 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3920 SquareToPos(row, column, &x, &y);
\r
3921 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3922 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3923 SelectObject(hdcmem, oldBrush);
\r
3928 if( appData.highlightMoveWithArrow ) {
\r
3929 DrawArrowHighlight(hdcmem);
\r
3932 DrawCoordsOnDC(hdcmem);
\r
3934 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3935 /* to make sure lastDrawn contains what is actually drawn */
\r
3937 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3938 if (dragged_piece != EmptySquare) {
\r
3939 /* [HGM] or restack */
\r
3940 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3941 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3943 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3944 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3945 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3946 x = dragInfo.pos.x - squareSize / 2;
\r
3947 y = dragInfo.pos.y - squareSize / 2;
\r
3948 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3949 ((int) dragInfo.piece < (int) BlackPawn),
\r
3950 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3953 /* Put the animated piece back into place and draw it */
\r
3954 if (animInfo.piece != EmptySquare) {
\r
3955 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3956 x = boardRect.left + animInfo.pos.x;
\r
3957 y = boardRect.top + animInfo.pos.y;
\r
3958 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3959 ((int) animInfo.piece < (int) BlackPawn),
\r
3960 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3963 /* Release the bufferBitmap by selecting in the old bitmap
\r
3964 * and delete the memory DC
\r
3966 SelectObject(hdcmem, oldBitmap);
\r
3969 /* Set clipping on the target DC */
\r
3970 if (!fullrepaint) {
\r
3971 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3973 GetRgnBox(clips[x], &rect);
\r
3974 DeleteObject(clips[x]);
\r
3975 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3976 rect.right + wpMain.width/2, rect.bottom);
\r
3978 SelectClipRgn(hdc, clips[0]);
\r
3979 for (x = 1; x < num_clips; x++) {
\r
3980 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3981 abort(); // this should never ever happen!
\r
3985 /* Copy the new bitmap onto the screen in one go.
\r
3986 * This way we avoid any flickering
\r
3988 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3989 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3990 boardRect.right - boardRect.left,
\r
3991 boardRect.bottom - boardRect.top,
\r
3992 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3993 if(saveDiagFlag) {
\r
3994 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3995 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3997 GetObject(bufferBitmap, sizeof(b), &b);
\r
3998 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3999 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4000 bih.biWidth = b.bmWidth;
\r
4001 bih.biHeight = b.bmHeight;
\r
4003 bih.biBitCount = b.bmBitsPixel;
\r
4004 bih.biCompression = 0;
\r
4005 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4006 bih.biXPelsPerMeter = 0;
\r
4007 bih.biYPelsPerMeter = 0;
\r
4008 bih.biClrUsed = 0;
\r
4009 bih.biClrImportant = 0;
\r
4010 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4011 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4012 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4013 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4015 wb = b.bmWidthBytes;
\r
4017 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4018 int k = ((int*) pData)[i];
\r
4019 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4020 if(j >= 16) break;
\r
4022 if(j >= nrColors) nrColors = j+1;
\r
4024 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4026 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4027 for(w=0; w<(wb>>2); w+=2) {
\r
4028 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4029 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4030 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4031 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4032 pData[p++] = m | j<<4;
\r
4034 while(p&3) pData[p++] = 0;
\r
4037 wb = ((wb+31)>>5)<<2;
\r
4039 // write BITMAPFILEHEADER
\r
4040 fprintf(diagFile, "BM");
\r
4041 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4042 fputDW(diagFile, 0);
\r
4043 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4044 // write BITMAPINFOHEADER
\r
4045 fputDW(diagFile, 40);
\r
4046 fputDW(diagFile, b.bmWidth);
\r
4047 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4048 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4049 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4050 fputDW(diagFile, 0);
\r
4051 fputDW(diagFile, 0);
\r
4052 fputDW(diagFile, 0);
\r
4053 fputDW(diagFile, 0);
\r
4054 fputDW(diagFile, 0);
\r
4055 fputDW(diagFile, 0);
\r
4056 // write color table
\r
4058 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4059 // write bitmap data
\r
4060 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4061 fputc(pData[i], diagFile);
\r
4066 SelectObject(tmphdc, oldBitmap);
\r
4068 /* Massive cleanup */
\r
4069 for (x = 0; x < num_clips; x++)
\r
4070 DeleteObject(clips[x]);
\r
4073 DeleteObject(bufferBitmap);
\r
4076 ReleaseDC(hwndMain, hdc);
\r
4078 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4080 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4082 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4085 /* CopyBoard(lastDrawn, board);*/
\r
4086 lastDrawnHighlight = highlightInfo;
\r
4087 lastDrawnPremove = premoveHighlightInfo;
\r
4088 lastDrawnFlipView = flipView;
\r
4089 lastDrawnValid[nr] = 1;
\r
4092 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4097 saveDiagFlag = 1; diagFile = f;
\r
4098 HDCDrawPosition(NULL, TRUE, NULL);
\r
4106 /*---------------------------------------------------------------------------*\
\r
4107 | CLIENT PAINT PROCEDURE
\r
4108 | This is the main event-handler for the WM_PAINT message.
\r
4110 \*---------------------------------------------------------------------------*/
\r
4112 PaintProc(HWND hwnd)
\r
4118 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4119 if (IsIconic(hwnd)) {
\r
4120 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4122 if (!appData.monoMode) {
\r
4123 SelectPalette(hdc, hPal, FALSE);
\r
4124 RealizePalette(hdc);
\r
4126 HDCDrawPosition(hdc, 1, NULL);
\r
4127 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4128 flipView = !flipView; partnerUp = !partnerUp;
\r
4129 HDCDrawPosition(hdc, 1, NULL);
\r
4130 flipView = !flipView; partnerUp = !partnerUp;
\r
4133 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4134 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4135 ETO_CLIPPED|ETO_OPAQUE,
\r
4136 &messageRect, messageText, strlen(messageText), NULL);
\r
4137 SelectObject(hdc, oldFont);
\r
4138 DisplayBothClocks();
\r
4141 EndPaint(hwnd,&ps);
\r
4149 * If the user selects on a border boundary, return -1; if off the board,
\r
4150 * return -2. Otherwise map the event coordinate to the square.
\r
4151 * The offset boardRect.left or boardRect.top must already have been
\r
4152 * subtracted from x.
\r
4154 int EventToSquare(x, limit)
\r
4159 if (x < lineGap + border)
\r
4161 x -= lineGap + border;
\r
4162 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4164 x /= (squareSize + lineGap);
\r
4176 DropEnable dropEnables[] = {
\r
4177 { 'P', DP_Pawn, N_("Pawn") },
\r
4178 { 'N', DP_Knight, N_("Knight") },
\r
4179 { 'B', DP_Bishop, N_("Bishop") },
\r
4180 { 'R', DP_Rook, N_("Rook") },
\r
4181 { 'Q', DP_Queen, N_("Queen") },
\r
4185 SetupDropMenu(HMENU hmenu)
\r
4187 int i, count, enable;
\r
4189 extern char white_holding[], black_holding[];
\r
4190 char item[MSG_SIZ];
\r
4192 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4193 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4194 dropEnables[i].piece);
\r
4196 while (p && *p++ == dropEnables[i].piece) count++;
\r
4197 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4198 enable = count > 0 || !appData.testLegality
\r
4199 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4200 && !appData.icsActive);
\r
4201 ModifyMenu(hmenu, dropEnables[i].command,
\r
4202 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4203 dropEnables[i].command, item);
\r
4207 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4209 dragInfo.lastpos.x = boardRect.left + x;
\r
4210 dragInfo.lastpos.y = boardRect.top + y;
\r
4211 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4212 dragInfo.from.x = fromX;
\r
4213 dragInfo.from.y = fromY;
\r
4214 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4215 dragInfo.start = dragInfo.from;
\r
4216 SetCapture(hwndMain);
\r
4219 void DragPieceEnd(int x, int y)
\r
4222 dragInfo.start.x = dragInfo.start.y = -1;
\r
4223 dragInfo.from = dragInfo.start;
\r
4224 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4227 void ChangeDragPiece(ChessSquare piece)
\r
4229 dragInfo.piece = piece;
\r
4232 /* Event handler for mouse messages */
\r
4234 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4238 static int recursive = 0;
\r
4240 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4243 if (message == WM_MBUTTONUP) {
\r
4244 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4245 to the middle button: we simulate pressing the left button too!
\r
4247 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4248 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4254 pt.x = LOWORD(lParam);
\r
4255 pt.y = HIWORD(lParam);
\r
4256 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4257 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4258 if (!flipView && y >= 0) {
\r
4259 y = BOARD_HEIGHT - 1 - y;
\r
4261 if (flipView && x >= 0) {
\r
4262 x = BOARD_WIDTH - 1 - x;
\r
4265 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4266 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4268 switch (message) {
\r
4269 case WM_LBUTTONDOWN:
\r
4270 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4271 ClockClick(flipClock); break;
\r
4272 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4273 ClockClick(!flipClock); break;
\r
4275 dragInfo.start.x = dragInfo.start.y = -1;
\r
4276 dragInfo.from = dragInfo.start;
\r
4277 if(fromX == -1 && frozen) { // not sure where this is for
\r
4278 fromX = fromY = -1;
\r
4279 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4282 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4283 DrawPosition(TRUE, NULL);
\r
4286 case WM_LBUTTONUP:
\r
4287 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4288 DrawPosition(TRUE, NULL);
\r
4291 case WM_MOUSEMOVE:
\r
4292 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4293 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4294 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4295 if ((appData.animateDragging || appData.highlightDragging)
\r
4296 && (wParam & MK_LBUTTON)
\r
4297 && dragInfo.from.x >= 0)
\r
4299 BOOL full_repaint = FALSE;
\r
4301 if (appData.animateDragging) {
\r
4302 dragInfo.pos = pt;
\r
4304 if (appData.highlightDragging) {
\r
4305 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4306 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4307 full_repaint = TRUE;
\r
4311 DrawPosition( full_repaint, NULL);
\r
4313 dragInfo.lastpos = dragInfo.pos;
\r
4317 case WM_MOUSEWHEEL: // [DM]
\r
4318 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4319 /* Mouse Wheel is being rolled forward
\r
4320 * Play moves forward
\r
4322 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4323 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4324 /* Mouse Wheel is being rolled backward
\r
4325 * Play moves backward
\r
4327 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4328 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4332 case WM_MBUTTONUP:
\r
4333 case WM_RBUTTONUP:
\r
4335 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4338 case WM_MBUTTONDOWN:
\r
4339 case WM_RBUTTONDOWN:
\r
4342 fromX = fromY = -1;
\r
4343 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4344 dragInfo.start.x = dragInfo.start.y = -1;
\r
4345 dragInfo.from = dragInfo.start;
\r
4346 dragInfo.lastpos = dragInfo.pos;
\r
4347 if (appData.highlightDragging) {
\r
4348 ClearHighlights();
\r
4351 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4352 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4353 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4354 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4355 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4359 DrawPosition(TRUE, NULL);
\r
4361 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4364 if (message == WM_MBUTTONDOWN) {
\r
4365 buttonCount = 3; /* even if system didn't think so */
\r
4366 if (wParam & MK_SHIFT)
\r
4367 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4369 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4370 } else { /* message == WM_RBUTTONDOWN */
\r
4371 /* Just have one menu, on the right button. Windows users don't
\r
4372 think to try the middle one, and sometimes other software steals
\r
4373 it, or it doesn't really exist. */
\r
4374 if(gameInfo.variant != VariantShogi)
\r
4375 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4377 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4381 SetCapture(hwndMain);
\r
4384 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4385 SetupDropMenu(hmenu);
\r
4386 MenuPopup(hwnd, pt, hmenu, -1);
\r
4396 /* Preprocess messages for buttons in main window */
\r
4398 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4400 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4403 for (i=0; i<N_BUTTONS; i++) {
\r
4404 if (buttonDesc[i].id == id) break;
\r
4406 if (i == N_BUTTONS) return 0;
\r
4407 switch (message) {
\r
4412 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4413 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4420 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4423 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4424 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4425 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4426 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4428 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4430 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4431 TypeInEvent((char)wParam);
\r
4437 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4440 /* Process messages for Promotion dialog box */
\r
4442 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4446 switch (message) {
\r
4447 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4448 /* Center the dialog over the application window */
\r
4449 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4450 Translate(hDlg, DLG_PromotionKing);
\r
4451 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4452 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4453 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4454 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4455 SW_SHOW : SW_HIDE);
\r
4456 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4457 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4458 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4459 PieceToChar(WhiteAngel) != '~') ||
\r
4460 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4461 PieceToChar(BlackAngel) != '~') ) ?
\r
4462 SW_SHOW : SW_HIDE);
\r
4463 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4464 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4465 PieceToChar(WhiteMarshall) != '~') ||
\r
4466 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4467 PieceToChar(BlackMarshall) != '~') ) ?
\r
4468 SW_SHOW : SW_HIDE);
\r
4469 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4470 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4471 gameInfo.variant != VariantShogi ?
\r
4472 SW_SHOW : SW_HIDE);
\r
4473 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4474 gameInfo.variant != VariantShogi ?
\r
4475 SW_SHOW : SW_HIDE);
\r
4476 if(gameInfo.variant == VariantShogi) {
\r
4477 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4478 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4479 SetWindowText(hDlg, "Promote?");
\r
4481 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4482 gameInfo.variant == VariantSuper ?
\r
4483 SW_SHOW : SW_HIDE);
\r
4486 case WM_COMMAND: /* message: received a command */
\r
4487 switch (LOWORD(wParam)) {
\r
4489 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4490 ClearHighlights();
\r
4491 DrawPosition(FALSE, NULL);
\r
4494 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4497 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4500 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4501 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4504 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4505 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4507 case PB_Chancellor:
\r
4508 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4510 case PB_Archbishop:
\r
4511 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4514 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4519 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4520 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4521 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4522 fromX = fromY = -1;
\r
4523 if (!appData.highlightLastMove) {
\r
4524 ClearHighlights();
\r
4525 DrawPosition(FALSE, NULL);
\r
4532 /* Pop up promotion dialog */
\r
4534 PromotionPopup(HWND hwnd)
\r
4538 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4539 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4540 hwnd, (DLGPROC)lpProc);
\r
4541 FreeProcInstance(lpProc);
\r
4547 DrawPosition(TRUE, NULL);
\r
4548 PromotionPopup(hwndMain);
\r
4552 LoadGameDialog(HWND hwnd, char* title)
\r
4556 char fileTitle[MSG_SIZ];
\r
4557 f = OpenFileDialog(hwnd, "rb", "",
\r
4558 appData.oldSaveStyle ? "gam" : "pgn",
\r
4560 title, &number, fileTitle, NULL);
\r
4562 cmailMsgLoaded = FALSE;
\r
4563 if (number == 0) {
\r
4564 int error = GameListBuild(f);
\r
4566 DisplayError(_("Cannot build game list"), error);
\r
4567 } else if (!ListEmpty(&gameList) &&
\r
4568 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4569 GameListPopUp(f, fileTitle);
\r
4572 GameListDestroy();
\r
4575 LoadGame(f, number, fileTitle, FALSE);
\r
4579 int get_term_width()
\r
4584 HFONT hfont, hold_font;
\r
4589 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4593 // get the text metrics
\r
4594 hdc = GetDC(hText);
\r
4595 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4596 if (consoleCF.dwEffects & CFE_BOLD)
\r
4597 lf.lfWeight = FW_BOLD;
\r
4598 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4599 lf.lfItalic = TRUE;
\r
4600 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4601 lf.lfStrikeOut = TRUE;
\r
4602 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4603 lf.lfUnderline = TRUE;
\r
4604 hfont = CreateFontIndirect(&lf);
\r
4605 hold_font = SelectObject(hdc, hfont);
\r
4606 GetTextMetrics(hdc, &tm);
\r
4607 SelectObject(hdc, hold_font);
\r
4608 DeleteObject(hfont);
\r
4609 ReleaseDC(hText, hdc);
\r
4611 // get the rectangle
\r
4612 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4614 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4617 void UpdateICSWidth(HWND hText)
\r
4619 LONG old_width, new_width;
\r
4621 new_width = get_term_width(hText, FALSE);
\r
4622 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4623 if (new_width != old_width)
\r
4625 ics_update_width(new_width);
\r
4626 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4631 ChangedConsoleFont()
\r
4634 CHARRANGE tmpsel, sel;
\r
4635 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4636 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4637 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4640 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4641 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4642 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4643 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4644 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4645 * size. This was undocumented in the version of MSVC++ that I had
\r
4646 * when I wrote the code, but is apparently documented now.
\r
4648 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4649 cfmt.bCharSet = f->lf.lfCharSet;
\r
4650 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4651 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4652 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4653 /* Why are the following seemingly needed too? */
\r
4654 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4655 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4656 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4658 tmpsel.cpMax = -1; /*999999?*/
\r
4659 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4660 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4661 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4662 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4664 paraf.cbSize = sizeof(paraf);
\r
4665 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4666 paraf.dxStartIndent = 0;
\r
4667 paraf.dxOffset = WRAP_INDENT;
\r
4668 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4669 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4670 UpdateICSWidth(hText);
\r
4673 /*---------------------------------------------------------------------------*\
\r
4675 * Window Proc for main window
\r
4677 \*---------------------------------------------------------------------------*/
\r
4679 /* Process messages for main window, etc. */
\r
4681 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4684 int wmId, wmEvent;
\r
4688 char fileTitle[MSG_SIZ];
\r
4689 static SnapData sd;
\r
4690 static int peek=0;
\r
4692 switch (message) {
\r
4694 case WM_PAINT: /* message: repaint portion of window */
\r
4698 case WM_ERASEBKGND:
\r
4699 if (IsIconic(hwnd)) {
\r
4700 /* Cheat; change the message */
\r
4701 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4703 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4707 case WM_LBUTTONDOWN:
\r
4708 case WM_MBUTTONDOWN:
\r
4709 case WM_RBUTTONDOWN:
\r
4710 case WM_LBUTTONUP:
\r
4711 case WM_MBUTTONUP:
\r
4712 case WM_RBUTTONUP:
\r
4713 case WM_MOUSEMOVE:
\r
4714 case WM_MOUSEWHEEL:
\r
4715 MouseEvent(hwnd, message, wParam, lParam);
\r
4719 if((char)wParam == '\b') {
\r
4720 ForwardEvent(); peek = 0;
\r
4723 JAWS_KBUP_NAVIGATION
\r
4728 if((char)wParam == '\b') {
\r
4729 if(!peek) BackwardEvent(), peek = 1;
\r
4732 JAWS_KBDOWN_NAVIGATION
\r
4738 JAWS_ALT_INTERCEPT
\r
4740 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4741 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4742 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4743 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4745 SendMessage(h, message, wParam, lParam);
\r
4746 } else if(lParam != KF_REPEAT) {
\r
4747 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4748 TypeInEvent((char)wParam);
\r
4749 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4750 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4755 case WM_PALETTECHANGED:
\r
4756 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4758 HDC hdc = GetDC(hwndMain);
\r
4759 SelectPalette(hdc, hPal, TRUE);
\r
4760 nnew = RealizePalette(hdc);
\r
4762 paletteChanged = TRUE;
\r
4764 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4766 ReleaseDC(hwnd, hdc);
\r
4770 case WM_QUERYNEWPALETTE:
\r
4771 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4773 HDC hdc = GetDC(hwndMain);
\r
4774 paletteChanged = FALSE;
\r
4775 SelectPalette(hdc, hPal, FALSE);
\r
4776 nnew = RealizePalette(hdc);
\r
4778 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4780 ReleaseDC(hwnd, hdc);
\r
4785 case WM_COMMAND: /* message: command from application menu */
\r
4786 wmId = LOWORD(wParam);
\r
4787 wmEvent = HIWORD(wParam);
\r
4792 SAY("new game enter a move to play against the computer with white");
\r
4795 case IDM_NewGameFRC:
\r
4796 if( NewGameFRC() == 0 ) {
\r
4801 case IDM_NewVariant:
\r
4802 NewVariantPopup(hwnd);
\r
4805 case IDM_LoadGame:
\r
4806 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4809 case IDM_LoadNextGame:
\r
4813 case IDM_LoadPrevGame:
\r
4817 case IDM_ReloadGame:
\r
4821 case IDM_LoadPosition:
\r
4822 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4823 Reset(FALSE, TRUE);
\r
4826 f = OpenFileDialog(hwnd, "rb", "",
\r
4827 appData.oldSaveStyle ? "pos" : "fen",
\r
4829 _("Load Position from File"), &number, fileTitle, NULL);
\r
4831 LoadPosition(f, number, fileTitle);
\r
4835 case IDM_LoadNextPosition:
\r
4836 ReloadPosition(1);
\r
4839 case IDM_LoadPrevPosition:
\r
4840 ReloadPosition(-1);
\r
4843 case IDM_ReloadPosition:
\r
4844 ReloadPosition(0);
\r
4847 case IDM_SaveGame:
\r
4848 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4849 f = OpenFileDialog(hwnd, "a", defName,
\r
4850 appData.oldSaveStyle ? "gam" : "pgn",
\r
4852 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4854 SaveGame(f, 0, "");
\r
4858 case IDM_SavePosition:
\r
4859 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4860 f = OpenFileDialog(hwnd, "a", defName,
\r
4861 appData.oldSaveStyle ? "pos" : "fen",
\r
4863 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4865 SavePosition(f, 0, "");
\r
4869 case IDM_SaveDiagram:
\r
4870 defName = "diagram";
\r
4871 f = OpenFileDialog(hwnd, "wb", defName,
\r
4874 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4880 case IDM_CreateBook:
\r
4881 CreateBookEvent();
\r
4884 case IDM_CopyGame:
\r
4885 CopyGameToClipboard();
\r
4888 case IDM_PasteGame:
\r
4889 PasteGameFromClipboard();
\r
4892 case IDM_CopyGameListToClipboard:
\r
4893 CopyGameListToClipboard();
\r
4896 /* [AS] Autodetect FEN or PGN data */
\r
4897 case IDM_PasteAny:
\r
4898 PasteGameOrFENFromClipboard();
\r
4901 /* [AS] Move history */
\r
4902 case IDM_ShowMoveHistory:
\r
4903 if( MoveHistoryIsUp() ) {
\r
4904 MoveHistoryPopDown();
\r
4907 MoveHistoryPopUp();
\r
4911 /* [AS] Eval graph */
\r
4912 case IDM_ShowEvalGraph:
\r
4913 if( EvalGraphIsUp() ) {
\r
4914 EvalGraphPopDown();
\r
4918 SetFocus(hwndMain);
\r
4922 /* [AS] Engine output */
\r
4923 case IDM_ShowEngineOutput:
\r
4924 if( EngineOutputIsUp() ) {
\r
4925 EngineOutputPopDown();
\r
4928 EngineOutputPopUp();
\r
4932 /* [AS] User adjudication */
\r
4933 case IDM_UserAdjudication_White:
\r
4934 UserAdjudicationEvent( +1 );
\r
4937 case IDM_UserAdjudication_Black:
\r
4938 UserAdjudicationEvent( -1 );
\r
4941 case IDM_UserAdjudication_Draw:
\r
4942 UserAdjudicationEvent( 0 );
\r
4945 /* [AS] Game list options dialog */
\r
4946 case IDM_GameListOptions:
\r
4947 GameListOptions();
\r
4954 case IDM_CopyPosition:
\r
4955 CopyFENToClipboard();
\r
4958 case IDM_PastePosition:
\r
4959 PasteFENFromClipboard();
\r
4962 case IDM_MailMove:
\r
4966 case IDM_ReloadCMailMsg:
\r
4967 Reset(TRUE, TRUE);
\r
4968 ReloadCmailMsgEvent(FALSE);
\r
4971 case IDM_Minimize:
\r
4972 ShowWindow(hwnd, SW_MINIMIZE);
\r
4979 case IDM_MachineWhite:
\r
4980 MachineWhiteEvent();
\r
4982 * refresh the tags dialog only if it's visible
\r
4984 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4986 tags = PGNTags(&gameInfo);
\r
4987 TagsPopUp(tags, CmailMsg());
\r
4990 SAY("computer starts playing white");
\r
4993 case IDM_MachineBlack:
\r
4994 MachineBlackEvent();
\r
4996 * refresh the tags dialog only if it's visible
\r
4998 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5000 tags = PGNTags(&gameInfo);
\r
5001 TagsPopUp(tags, CmailMsg());
\r
5004 SAY("computer starts playing black");
\r
5007 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5008 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5011 case IDM_TwoMachines:
\r
5012 TwoMachinesEvent();
\r
5014 * refresh the tags dialog only if it's visible
\r
5016 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5018 tags = PGNTags(&gameInfo);
\r
5019 TagsPopUp(tags, CmailMsg());
\r
5022 SAY("computer starts playing both sides");
\r
5025 case IDM_AnalysisMode:
\r
5026 if(AnalyzeModeEvent()) {
\r
5027 SAY("analyzing current position");
\r
5031 case IDM_AnalyzeFile:
\r
5032 AnalyzeFileEvent();
\r
5035 case IDM_IcsClient:
\r
5039 case IDM_EditGame:
\r
5040 case IDM_EditGame2:
\r
5045 case IDM_EditPosition:
\r
5046 case IDM_EditPosition2:
\r
5047 EditPositionEvent();
\r
5048 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5051 case IDM_Training:
\r
5055 case IDM_ShowGameList:
\r
5056 ShowGameListProc();
\r
5059 case IDM_EditProgs1:
\r
5060 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5063 case IDM_LoadProg1:
\r
5064 LoadEnginePopUp(hwndMain, 0);
\r
5067 case IDM_LoadProg2:
\r
5068 LoadEnginePopUp(hwndMain, 1);
\r
5071 case IDM_EditServers:
\r
5072 EditTagsPopUp(icsNames, &icsNames);
\r
5075 case IDM_EditTags:
\r
5080 case IDM_EditBook:
\r
5084 case IDM_EditComment:
\r
5086 if (commentUp && editComment) {
\r
5089 EditCommentEvent();
\r
5109 case IDM_CallFlag:
\r
5129 case IDM_StopObserving:
\r
5130 StopObservingEvent();
\r
5133 case IDM_StopExamining:
\r
5134 StopExaminingEvent();
\r
5138 UploadGameEvent();
\r
5141 case IDM_TypeInMove:
\r
5142 TypeInEvent('\000');
\r
5145 case IDM_TypeInName:
\r
5146 PopUpNameDialog('\000');
\r
5149 case IDM_Backward:
\r
5151 SetFocus(hwndMain);
\r
5158 SetFocus(hwndMain);
\r
5163 SetFocus(hwndMain);
\r
5168 SetFocus(hwndMain);
\r
5171 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5172 case OPT_GameListPrev:
\r
5173 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5177 RevertEvent(FALSE);
\r
5180 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5181 RevertEvent(TRUE);
\r
5184 case IDM_TruncateGame:
\r
5185 TruncateGameEvent();
\r
5192 case IDM_RetractMove:
\r
5193 RetractMoveEvent();
\r
5196 case IDM_FlipView:
\r
5197 flipView = !flipView;
\r
5198 DrawPosition(FALSE, NULL);
\r
5201 case IDM_FlipClock:
\r
5202 flipClock = !flipClock;
\r
5203 DisplayBothClocks();
\r
5207 case IDM_MuteSounds:
\r
5208 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5209 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5210 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5213 case IDM_GeneralOptions:
\r
5214 GeneralOptionsPopup(hwnd);
\r
5215 DrawPosition(TRUE, NULL);
\r
5218 case IDM_BoardOptions:
\r
5219 BoardOptionsPopup(hwnd);
\r
5222 case IDM_ThemeOptions:
\r
5223 ThemeOptionsPopup(hwnd);
\r
5226 case IDM_EnginePlayOptions:
\r
5227 EnginePlayOptionsPopup(hwnd);
\r
5230 case IDM_Engine1Options:
\r
5231 EngineOptionsPopup(hwnd, &first);
\r
5234 case IDM_Engine2Options:
\r
5236 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5237 EngineOptionsPopup(hwnd, &second);
\r
5240 case IDM_OptionsUCI:
\r
5241 UciOptionsPopup(hwnd);
\r
5245 TourneyPopup(hwnd);
\r
5248 case IDM_IcsOptions:
\r
5249 IcsOptionsPopup(hwnd);
\r
5253 FontsOptionsPopup(hwnd);
\r
5257 SoundOptionsPopup(hwnd);
\r
5260 case IDM_CommPort:
\r
5261 CommPortOptionsPopup(hwnd);
\r
5264 case IDM_LoadOptions:
\r
5265 LoadOptionsPopup(hwnd);
\r
5268 case IDM_SaveOptions:
\r
5269 SaveOptionsPopup(hwnd);
\r
5272 case IDM_TimeControl:
\r
5273 TimeControlOptionsPopup(hwnd);
\r
5276 case IDM_SaveSettings:
\r
5277 SaveSettings(settingsFileName);
\r
5280 case IDM_SaveSettingsOnExit:
\r
5281 saveSettingsOnExit = !saveSettingsOnExit;
\r
5282 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5283 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5284 MF_CHECKED : MF_UNCHECKED));
\r
5295 case IDM_AboutGame:
\r
5300 appData.debugMode = !appData.debugMode;
\r
5301 if (appData.debugMode) {
\r
5302 char dir[MSG_SIZ];
\r
5303 GetCurrentDirectory(MSG_SIZ, dir);
\r
5304 SetCurrentDirectory(installDir);
\r
5305 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5306 SetCurrentDirectory(dir);
\r
5307 setbuf(debugFP, NULL);
\r
5314 case IDM_HELPCONTENTS:
\r
5315 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5316 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5317 MessageBox (GetFocus(),
\r
5318 _("Unable to activate help"),
\r
5319 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5323 case IDM_HELPSEARCH:
\r
5324 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5325 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5326 MessageBox (GetFocus(),
\r
5327 _("Unable to activate help"),
\r
5328 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5332 case IDM_HELPHELP:
\r
5333 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5334 MessageBox (GetFocus(),
\r
5335 _("Unable to activate help"),
\r
5336 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5341 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5343 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5344 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5345 FreeProcInstance(lpProc);
\r
5348 case IDM_DirectCommand1:
\r
5349 AskQuestionEvent(_("Direct Command"),
\r
5350 _("Send to chess program:"), "", "1");
\r
5352 case IDM_DirectCommand2:
\r
5353 AskQuestionEvent(_("Direct Command"),
\r
5354 _("Send to second chess program:"), "", "2");
\r
5357 case EP_WhitePawn:
\r
5358 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5362 case EP_WhiteKnight:
\r
5363 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5364 fromX = fromY = -1;
\r
5367 case EP_WhiteBishop:
\r
5368 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5369 fromX = fromY = -1;
\r
5372 case EP_WhiteRook:
\r
5373 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5374 fromX = fromY = -1;
\r
5377 case EP_WhiteQueen:
\r
5378 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5379 fromX = fromY = -1;
\r
5382 case EP_WhiteFerz:
\r
5383 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5387 case EP_WhiteWazir:
\r
5388 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5392 case EP_WhiteAlfil:
\r
5393 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5394 fromX = fromY = -1;
\r
5397 case EP_WhiteCannon:
\r
5398 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5402 case EP_WhiteCardinal:
\r
5403 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5404 fromX = fromY = -1;
\r
5407 case EP_WhiteMarshall:
\r
5408 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5409 fromX = fromY = -1;
\r
5412 case EP_WhiteKing:
\r
5413 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5414 fromX = fromY = -1;
\r
5417 case EP_BlackPawn:
\r
5418 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5419 fromX = fromY = -1;
\r
5422 case EP_BlackKnight:
\r
5423 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5424 fromX = fromY = -1;
\r
5427 case EP_BlackBishop:
\r
5428 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5429 fromX = fromY = -1;
\r
5432 case EP_BlackRook:
\r
5433 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5434 fromX = fromY = -1;
\r
5437 case EP_BlackQueen:
\r
5438 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5439 fromX = fromY = -1;
\r
5442 case EP_BlackFerz:
\r
5443 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5444 fromX = fromY = -1;
\r
5447 case EP_BlackWazir:
\r
5448 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5449 fromX = fromY = -1;
\r
5452 case EP_BlackAlfil:
\r
5453 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5454 fromX = fromY = -1;
\r
5457 case EP_BlackCannon:
\r
5458 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5459 fromX = fromY = -1;
\r
5462 case EP_BlackCardinal:
\r
5463 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5464 fromX = fromY = -1;
\r
5467 case EP_BlackMarshall:
\r
5468 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5469 fromX = fromY = -1;
\r
5472 case EP_BlackKing:
\r
5473 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5474 fromX = fromY = -1;
\r
5477 case EP_EmptySquare:
\r
5478 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5479 fromX = fromY = -1;
\r
5482 case EP_ClearBoard:
\r
5483 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5484 fromX = fromY = -1;
\r
5488 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5489 fromX = fromY = -1;
\r
5493 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5494 fromX = fromY = -1;
\r
5498 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5499 fromX = fromY = -1;
\r
5503 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5504 fromX = fromY = -1;
\r
5508 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5509 fromX = fromY = -1;
\r
5513 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5514 fromX = fromY = -1;
\r
5518 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5519 fromX = fromY = -1;
\r
5523 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5524 fromX = fromY = -1;
\r
5528 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5529 fromX = fromY = -1;
\r
5533 barbaric = 0; appData.language = "";
\r
5534 TranslateMenus(0);
\r
5535 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5536 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5537 lastChecked = wmId;
\r
5541 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5542 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5544 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5545 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5546 TranslateMenus(0);
\r
5547 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5548 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5549 lastChecked = wmId;
\r
5552 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5558 case CLOCK_TIMER_ID:
\r
5559 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5560 clockTimerEvent = 0;
\r
5561 DecrementClocks(); /* call into back end */
\r
5563 case LOAD_GAME_TIMER_ID:
\r
5564 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5565 loadGameTimerEvent = 0;
\r
5566 AutoPlayGameLoop(); /* call into back end */
\r
5568 case ANALYSIS_TIMER_ID:
\r
5569 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5570 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5571 AnalysisPeriodicEvent(0);
\r
5573 KillTimer(hwnd, analysisTimerEvent);
\r
5574 analysisTimerEvent = 0;
\r
5577 case DELAYED_TIMER_ID:
\r
5578 KillTimer(hwnd, delayedTimerEvent);
\r
5579 delayedTimerEvent = 0;
\r
5580 delayedTimerCallback();
\r
5585 case WM_USER_Input:
\r
5586 InputEvent(hwnd, message, wParam, lParam);
\r
5589 /* [AS] Also move "attached" child windows */
\r
5590 case WM_WINDOWPOSCHANGING:
\r
5592 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5593 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5595 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5596 /* Window is moving */
\r
5599 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5600 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5601 rcMain.right = wpMain.x + wpMain.width;
\r
5602 rcMain.top = wpMain.y;
\r
5603 rcMain.bottom = wpMain.y + wpMain.height;
\r
5605 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5606 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5607 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5608 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5609 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5610 wpMain.x = lpwp->x;
\r
5611 wpMain.y = lpwp->y;
\r
5616 /* [AS] Snapping */
\r
5617 case WM_ENTERSIZEMOVE:
\r
5618 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5619 if (hwnd == hwndMain) {
\r
5620 doingSizing = TRUE;
\r
5623 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5627 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5628 if (hwnd == hwndMain) {
\r
5629 lastSizing = wParam;
\r
5634 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5635 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5637 case WM_EXITSIZEMOVE:
\r
5638 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5639 if (hwnd == hwndMain) {
\r
5641 doingSizing = FALSE;
\r
5642 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5643 GetClientRect(hwnd, &client);
\r
5644 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5646 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5648 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5651 case WM_DESTROY: /* message: window being destroyed */
\r
5652 PostQuitMessage(0);
\r
5656 if (hwnd == hwndMain) {
\r
5661 default: /* Passes it on if unprocessed */
\r
5662 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5667 /*---------------------------------------------------------------------------*\
\r
5669 * Misc utility routines
\r
5671 \*---------------------------------------------------------------------------*/
\r
5674 * Decent random number generator, at least not as bad as Windows
\r
5675 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5677 unsigned int randstate;
\r
5682 randstate = randstate * 1664525 + 1013904223;
\r
5683 return (int) randstate & 0x7fffffff;
\r
5687 mysrandom(unsigned int seed)
\r
5694 * returns TRUE if user selects a different color, FALSE otherwise
\r
5698 ChangeColor(HWND hwnd, COLORREF *which)
\r
5700 static BOOL firstTime = TRUE;
\r
5701 static DWORD customColors[16];
\r
5703 COLORREF newcolor;
\r
5708 /* Make initial colors in use available as custom colors */
\r
5709 /* Should we put the compiled-in defaults here instead? */
\r
5711 customColors[i++] = lightSquareColor & 0xffffff;
\r
5712 customColors[i++] = darkSquareColor & 0xffffff;
\r
5713 customColors[i++] = whitePieceColor & 0xffffff;
\r
5714 customColors[i++] = blackPieceColor & 0xffffff;
\r
5715 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5716 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5718 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5719 customColors[i++] = textAttribs[ccl].color;
\r
5721 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5722 firstTime = FALSE;
\r
5725 cc.lStructSize = sizeof(cc);
\r
5726 cc.hwndOwner = hwnd;
\r
5727 cc.hInstance = NULL;
\r
5728 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5729 cc.lpCustColors = (LPDWORD) customColors;
\r
5730 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5732 if (!ChooseColor(&cc)) return FALSE;
\r
5734 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5735 if (newcolor == *which) return FALSE;
\r
5736 *which = newcolor;
\r
5740 InitDrawingColors();
\r
5741 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5746 MyLoadSound(MySound *ms)
\r
5752 if (ms->data && ms->flag) free(ms->data);
\r
5755 switch (ms->name[0]) {
\r
5761 /* System sound from Control Panel. Don't preload here. */
\r
5765 if (ms->name[1] == NULLCHAR) {
\r
5766 /* "!" alone = silence */
\r
5769 /* Builtin wave resource. Error if not found. */
\r
5770 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5771 if (h == NULL) break;
\r
5772 ms->data = (void *)LoadResource(hInst, h);
\r
5773 ms->flag = 0; // not maloced, so cannot be freed!
\r
5774 if (h == NULL) break;
\r
5779 /* .wav file. Error if not found. */
\r
5780 f = fopen(ms->name, "rb");
\r
5781 if (f == NULL) break;
\r
5782 if (fstat(fileno(f), &st) < 0) break;
\r
5783 ms->data = malloc(st.st_size);
\r
5785 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5791 char buf[MSG_SIZ];
\r
5792 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5793 DisplayError(buf, GetLastError());
\r
5799 MyPlaySound(MySound *ms)
\r
5801 BOOLEAN ok = FALSE;
\r
5803 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5804 switch (ms->name[0]) {
\r
5806 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5811 /* System sound from Control Panel (deprecated feature).
\r
5812 "$" alone or an unset sound name gets default beep (still in use). */
\r
5813 if (ms->name[1]) {
\r
5814 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5816 if (!ok) ok = MessageBeep(MB_OK);
\r
5819 /* Builtin wave resource, or "!" alone for silence */
\r
5820 if (ms->name[1]) {
\r
5821 if (ms->data == NULL) return FALSE;
\r
5822 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5828 /* .wav file. Error if not found. */
\r
5829 if (ms->data == NULL) return FALSE;
\r
5830 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5833 /* Don't print an error: this can happen innocently if the sound driver
\r
5834 is busy; for instance, if another instance of WinBoard is playing
\r
5835 a sound at about the same time. */
\r
5841 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5844 OPENFILENAME *ofn;
\r
5845 static UINT *number; /* gross that this is static */
\r
5847 switch (message) {
\r
5848 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5849 /* Center the dialog over the application window */
\r
5850 ofn = (OPENFILENAME *) lParam;
\r
5851 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5852 number = (UINT *) ofn->lCustData;
\r
5853 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5857 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5858 Translate(hDlg, 1536);
\r
5859 return FALSE; /* Allow for further processing */
\r
5862 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5863 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5865 return FALSE; /* Allow for further processing */
\r
5871 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5873 static UINT *number;
\r
5874 OPENFILENAME *ofname;
\r
5877 case WM_INITDIALOG:
\r
5878 Translate(hdlg, DLG_IndexNumber);
\r
5879 ofname = (OPENFILENAME *)lParam;
\r
5880 number = (UINT *)(ofname->lCustData);
\r
5883 ofnot = (OFNOTIFY *)lParam;
\r
5884 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5885 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5894 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5895 char *nameFilt, char *dlgTitle, UINT *number,
\r
5896 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5898 OPENFILENAME openFileName;
\r
5899 char buf1[MSG_SIZ];
\r
5902 if (fileName == NULL) fileName = buf1;
\r
5903 if (defName == NULL) {
\r
5904 safeStrCpy(fileName, "*.", 3 );
\r
5905 strcat(fileName, defExt);
\r
5907 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5909 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5910 if (number) *number = 0;
\r
5912 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5913 openFileName.hwndOwner = hwnd;
\r
5914 openFileName.hInstance = (HANDLE) hInst;
\r
5915 openFileName.lpstrFilter = nameFilt;
\r
5916 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5917 openFileName.nMaxCustFilter = 0L;
\r
5918 openFileName.nFilterIndex = 1L;
\r
5919 openFileName.lpstrFile = fileName;
\r
5920 openFileName.nMaxFile = MSG_SIZ;
\r
5921 openFileName.lpstrFileTitle = fileTitle;
\r
5922 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5923 openFileName.lpstrInitialDir = NULL;
\r
5924 openFileName.lpstrTitle = dlgTitle;
\r
5925 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5926 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5927 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5928 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5929 openFileName.nFileOffset = 0;
\r
5930 openFileName.nFileExtension = 0;
\r
5931 openFileName.lpstrDefExt = defExt;
\r
5932 openFileName.lCustData = (LONG) number;
\r
5933 openFileName.lpfnHook = oldDialog ?
\r
5934 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5935 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5937 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5938 GetOpenFileName(&openFileName)) {
\r
5939 /* open the file */
\r
5940 f = fopen(openFileName.lpstrFile, write);
\r
5942 MessageBox(hwnd, _("File open failed"), NULL,
\r
5943 MB_OK|MB_ICONEXCLAMATION);
\r
5947 int err = CommDlgExtendedError();
\r
5948 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5957 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5959 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5962 * Get the first pop-up menu in the menu template. This is the
\r
5963 * menu that TrackPopupMenu displays.
\r
5965 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5966 TranslateOneMenu(10, hmenuTrackPopup);
\r
5968 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5971 * TrackPopup uses screen coordinates, so convert the
\r
5972 * coordinates of the mouse click to screen coordinates.
\r
5974 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5976 /* Draw and track the floating pop-up menu. */
\r
5977 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5978 pt.x, pt.y, 0, hwnd, NULL);
\r
5980 /* Destroy the menu.*/
\r
5981 DestroyMenu(hmenu);
\r
5986 int sizeX, sizeY, newSizeX, newSizeY;
\r
5988 } ResizeEditPlusButtonsClosure;
\r
5991 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5993 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5997 if (hChild == cl->hText) return TRUE;
\r
5998 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5999 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6000 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6001 ScreenToClient(cl->hDlg, &pt);
\r
6002 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6003 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6007 /* Resize a dialog that has a (rich) edit field filling most of
\r
6008 the top, with a row of buttons below */
\r
6010 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6013 int newTextHeight, newTextWidth;
\r
6014 ResizeEditPlusButtonsClosure cl;
\r
6016 /*if (IsIconic(hDlg)) return;*/
\r
6017 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6019 cl.hdwp = BeginDeferWindowPos(8);
\r
6021 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6022 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6023 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6024 if (newTextHeight < 0) {
\r
6025 newSizeY += -newTextHeight;
\r
6026 newTextHeight = 0;
\r
6028 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6029 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6035 cl.newSizeX = newSizeX;
\r
6036 cl.newSizeY = newSizeY;
\r
6037 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6039 EndDeferWindowPos(cl.hdwp);
\r
6042 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6044 RECT rChild, rParent;
\r
6045 int wChild, hChild, wParent, hParent;
\r
6046 int wScreen, hScreen, xNew, yNew;
\r
6049 /* Get the Height and Width of the child window */
\r
6050 GetWindowRect (hwndChild, &rChild);
\r
6051 wChild = rChild.right - rChild.left;
\r
6052 hChild = rChild.bottom - rChild.top;
\r
6054 /* Get the Height and Width of the parent window */
\r
6055 GetWindowRect (hwndParent, &rParent);
\r
6056 wParent = rParent.right - rParent.left;
\r
6057 hParent = rParent.bottom - rParent.top;
\r
6059 /* Get the display limits */
\r
6060 hdc = GetDC (hwndChild);
\r
6061 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6062 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6063 ReleaseDC(hwndChild, hdc);
\r
6065 /* Calculate new X position, then adjust for screen */
\r
6066 xNew = rParent.left + ((wParent - wChild) /2);
\r
6069 } else if ((xNew+wChild) > wScreen) {
\r
6070 xNew = wScreen - wChild;
\r
6073 /* Calculate new Y position, then adjust for screen */
\r
6075 yNew = rParent.top + ((hParent - hChild) /2);
\r
6078 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6083 } else if ((yNew+hChild) > hScreen) {
\r
6084 yNew = hScreen - hChild;
\r
6087 /* Set it, and return */
\r
6088 return SetWindowPos (hwndChild, NULL,
\r
6089 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6092 /* Center one window over another */
\r
6093 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6095 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6098 /*---------------------------------------------------------------------------*\
\r
6100 * Startup Dialog functions
\r
6102 \*---------------------------------------------------------------------------*/
\r
6104 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6106 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6108 while (*cd != NULL) {
\r
6109 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6115 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6117 char buf1[MAX_ARG_LEN];
\r
6120 if (str[0] == '@') {
\r
6121 FILE* f = fopen(str + 1, "r");
\r
6123 DisplayFatalError(str + 1, errno, 2);
\r
6126 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6128 buf1[len] = NULLCHAR;
\r
6132 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6135 char buf[MSG_SIZ];
\r
6136 char *end = strchr(str, '\n');
\r
6137 if (end == NULL) return;
\r
6138 memcpy(buf, str, end - str);
\r
6139 buf[end - str] = NULLCHAR;
\r
6140 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6146 SetStartupDialogEnables(HWND hDlg)
\r
6148 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6149 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6150 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6151 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6152 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6153 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6154 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6155 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6156 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6157 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6158 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6159 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6160 IsDlgButtonChecked(hDlg, OPT_View));
\r
6164 QuoteForFilename(char *filename)
\r
6166 int dquote, space;
\r
6167 dquote = strchr(filename, '"') != NULL;
\r
6168 space = strchr(filename, ' ') != NULL;
\r
6169 if (dquote || space) {
\r
6181 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6183 char buf[MSG_SIZ];
\r
6186 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6187 q = QuoteForFilename(nthcp);
\r
6188 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6189 if (*nthdir != NULLCHAR) {
\r
6190 q = QuoteForFilename(nthdir);
\r
6191 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6193 if (*nthcp == NULLCHAR) {
\r
6194 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6195 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6196 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6197 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6202 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6204 char buf[MSG_SIZ];
\r
6208 switch (message) {
\r
6209 case WM_INITDIALOG:
\r
6210 /* Center the dialog */
\r
6211 CenterWindow (hDlg, GetDesktopWindow());
\r
6212 Translate(hDlg, DLG_Startup);
\r
6213 /* Initialize the dialog items */
\r
6214 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6215 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6216 firstChessProgramNames);
\r
6217 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6218 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6219 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6220 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6221 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6222 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6223 if (*appData.icsHelper != NULLCHAR) {
\r
6224 char *q = QuoteForFilename(appData.icsHelper);
\r
6225 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6227 if (*appData.icsHost == NULLCHAR) {
\r
6228 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6229 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6230 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6231 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6232 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6235 if (appData.icsActive) {
\r
6236 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6238 else if (appData.noChessProgram) {
\r
6239 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6242 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6245 SetStartupDialogEnables(hDlg);
\r
6249 switch (LOWORD(wParam)) {
\r
6251 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6252 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6253 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6255 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6256 ParseArgs(StringGet, &p);
\r
6257 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6258 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6260 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6261 ParseArgs(StringGet, &p);
\r
6262 SwapEngines(singleList); // ... and then make it 'second'
\r
6263 appData.noChessProgram = FALSE;
\r
6264 appData.icsActive = FALSE;
\r
6265 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6266 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6267 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6269 ParseArgs(StringGet, &p);
\r
6270 if (appData.zippyPlay) {
\r
6271 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6272 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6274 ParseArgs(StringGet, &p);
\r
6276 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6277 appData.noChessProgram = TRUE;
\r
6278 appData.icsActive = FALSE;
\r
6280 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6281 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6284 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6285 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6287 ParseArgs(StringGet, &p);
\r
6289 EndDialog(hDlg, TRUE);
\r
6296 case IDM_HELPCONTENTS:
\r
6297 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6298 MessageBox (GetFocus(),
\r
6299 _("Unable to activate help"),
\r
6300 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6305 SetStartupDialogEnables(hDlg);
\r
6313 /*---------------------------------------------------------------------------*\
\r
6315 * About box dialog functions
\r
6317 \*---------------------------------------------------------------------------*/
\r
6319 /* Process messages for "About" dialog box */
\r
6321 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6323 switch (message) {
\r
6324 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6325 /* Center the dialog over the application window */
\r
6326 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6327 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6328 Translate(hDlg, ABOUTBOX);
\r
6332 case WM_COMMAND: /* message: received a command */
\r
6333 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6334 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6335 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6343 /*---------------------------------------------------------------------------*\
\r
6345 * Comment Dialog functions
\r
6347 \*---------------------------------------------------------------------------*/
\r
6350 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6352 static HANDLE hwndText = NULL;
\r
6353 int len, newSizeX, newSizeY, flags;
\r
6354 static int sizeX, sizeY;
\r
6359 switch (message) {
\r
6360 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6361 /* Initialize the dialog items */
\r
6362 Translate(hDlg, DLG_EditComment);
\r
6363 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6364 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6365 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6366 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6367 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6368 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6369 SetWindowText(hDlg, commentTitle);
\r
6370 if (editComment) {
\r
6371 SetFocus(hwndText);
\r
6373 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6375 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6376 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6377 MAKELPARAM(FALSE, 0));
\r
6378 /* Size and position the dialog */
\r
6379 if (!commentDialog) {
\r
6380 commentDialog = hDlg;
\r
6381 flags = SWP_NOZORDER;
\r
6382 GetClientRect(hDlg, &rect);
\r
6383 sizeX = rect.right;
\r
6384 sizeY = rect.bottom;
\r
6385 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6386 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6387 WINDOWPLACEMENT wp;
\r
6388 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6389 wp.length = sizeof(WINDOWPLACEMENT);
\r
6391 wp.showCmd = SW_SHOW;
\r
6392 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6393 wp.rcNormalPosition.left = wpComment.x;
\r
6394 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6395 wp.rcNormalPosition.top = wpComment.y;
\r
6396 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6397 SetWindowPlacement(hDlg, &wp);
\r
6399 GetClientRect(hDlg, &rect);
\r
6400 newSizeX = rect.right;
\r
6401 newSizeY = rect.bottom;
\r
6402 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6403 newSizeX, newSizeY);
\r
6408 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6411 case WM_COMMAND: /* message: received a command */
\r
6412 switch (LOWORD(wParam)) {
\r
6414 if (editComment) {
\r
6416 /* Read changed options from the dialog box */
\r
6417 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6418 len = GetWindowTextLength(hwndText);
\r
6419 str = (char *) malloc(len + 1);
\r
6420 GetWindowText(hwndText, str, len + 1);
\r
6429 ReplaceComment(commentIndex, str);
\r
6436 case OPT_CancelComment:
\r
6440 case OPT_ClearComment:
\r
6441 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6444 case OPT_EditComment:
\r
6445 EditCommentEvent();
\r
6453 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6454 if( wParam == OPT_CommentText ) {
\r
6455 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6457 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6458 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6462 pt.x = LOWORD( lpMF->lParam );
\r
6463 pt.y = HIWORD( lpMF->lParam );
\r
6465 if(lpMF->msg == WM_CHAR) {
\r
6467 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6468 index = sel.cpMin;
\r
6470 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6472 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6473 len = GetWindowTextLength(hwndText);
\r
6474 str = (char *) malloc(len + 1);
\r
6475 GetWindowText(hwndText, str, len + 1);
\r
6476 ReplaceComment(commentIndex, str);
\r
6477 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6478 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6481 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6482 lpMF->msg = WM_USER;
\r
6490 newSizeX = LOWORD(lParam);
\r
6491 newSizeY = HIWORD(lParam);
\r
6492 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6497 case WM_GETMINMAXINFO:
\r
6498 /* Prevent resizing window too small */
\r
6499 mmi = (MINMAXINFO *) lParam;
\r
6500 mmi->ptMinTrackSize.x = 100;
\r
6501 mmi->ptMinTrackSize.y = 100;
\r
6508 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6513 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6515 if (str == NULL) str = "";
\r
6516 p = (char *) malloc(2 * strlen(str) + 2);
\r
6519 if (*str == '\n') *q++ = '\r';
\r
6523 if (commentText != NULL) free(commentText);
\r
6525 commentIndex = index;
\r
6526 commentTitle = title;
\r
6528 editComment = edit;
\r
6530 if (commentDialog) {
\r
6531 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6532 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6534 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6535 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6536 hwndMain, (DLGPROC)lpProc);
\r
6537 FreeProcInstance(lpProc);
\r
6543 /*---------------------------------------------------------------------------*\
\r
6545 * Type-in move dialog functions
\r
6547 \*---------------------------------------------------------------------------*/
\r
6550 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6552 char move[MSG_SIZ];
\r
6555 switch (message) {
\r
6556 case WM_INITDIALOG:
\r
6557 move[0] = (char) lParam;
\r
6558 move[1] = NULLCHAR;
\r
6559 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6560 Translate(hDlg, DLG_TypeInMove);
\r
6561 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6562 SetWindowText(hInput, move);
\r
6564 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6568 switch (LOWORD(wParam)) {
\r
6571 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6572 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6573 TypeInDoneEvent(move);
\r
6574 EndDialog(hDlg, TRUE);
\r
6577 EndDialog(hDlg, FALSE);
\r
6588 PopUpMoveDialog(char firstchar)
\r
6592 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6593 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6594 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6595 FreeProcInstance(lpProc);
\r
6598 /*---------------------------------------------------------------------------*\
\r
6600 * Type-in name dialog functions
\r
6602 \*---------------------------------------------------------------------------*/
\r
6605 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6607 char move[MSG_SIZ];
\r
6610 switch (message) {
\r
6611 case WM_INITDIALOG:
\r
6612 move[0] = (char) lParam;
\r
6613 move[1] = NULLCHAR;
\r
6614 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6615 Translate(hDlg, DLG_TypeInName);
\r
6616 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6617 SetWindowText(hInput, move);
\r
6619 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6623 switch (LOWORD(wParam)) {
\r
6625 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6626 appData.userName = strdup(move);
\r
6629 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6630 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6631 DisplayTitle(move);
\r
6635 EndDialog(hDlg, TRUE);
\r
6638 EndDialog(hDlg, FALSE);
\r
6649 PopUpNameDialog(char firstchar)
\r
6653 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6654 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6655 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6656 FreeProcInstance(lpProc);
\r
6659 /*---------------------------------------------------------------------------*\
\r
6663 \*---------------------------------------------------------------------------*/
\r
6665 /* Nonmodal error box */
\r
6666 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6667 WPARAM wParam, LPARAM lParam);
\r
6670 ErrorPopUp(char *title, char *content)
\r
6674 BOOLEAN modal = hwndMain == NULL;
\r
6692 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6693 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6696 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6698 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6699 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6700 hwndMain, (DLGPROC)lpProc);
\r
6701 FreeProcInstance(lpProc);
\r
6708 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6709 if (errorDialog == NULL) return;
\r
6710 DestroyWindow(errorDialog);
\r
6711 errorDialog = NULL;
\r
6712 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6716 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6721 switch (message) {
\r
6722 case WM_INITDIALOG:
\r
6723 GetWindowRect(hDlg, &rChild);
\r
6726 SetWindowPos(hDlg, NULL, rChild.left,
\r
6727 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6728 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6732 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6733 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6734 and it doesn't work when you resize the dialog.
\r
6735 For now, just give it a default position.
\r
6737 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6738 Translate(hDlg, DLG_Error);
\r
6740 errorDialog = hDlg;
\r
6741 SetWindowText(hDlg, errorTitle);
\r
6742 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6743 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6747 switch (LOWORD(wParam)) {
\r
6750 if (errorDialog == hDlg) errorDialog = NULL;
\r
6751 DestroyWindow(hDlg);
\r
6763 HWND gothicDialog = NULL;
\r
6766 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6770 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6772 switch (message) {
\r
6773 case WM_INITDIALOG:
\r
6774 GetWindowRect(hDlg, &rChild);
\r
6776 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6780 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6781 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6782 and it doesn't work when you resize the dialog.
\r
6783 For now, just give it a default position.
\r
6785 gothicDialog = hDlg;
\r
6786 SetWindowText(hDlg, errorTitle);
\r
6787 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6788 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6792 switch (LOWORD(wParam)) {
\r
6795 if (errorDialog == hDlg) errorDialog = NULL;
\r
6796 DestroyWindow(hDlg);
\r
6808 GothicPopUp(char *title, VariantClass variant)
\r
6811 static char *lastTitle;
\r
6813 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6814 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6816 if(lastTitle != title && gothicDialog != NULL) {
\r
6817 DestroyWindow(gothicDialog);
\r
6818 gothicDialog = NULL;
\r
6820 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6821 title = lastTitle;
\r
6822 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6823 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6824 hwndMain, (DLGPROC)lpProc);
\r
6825 FreeProcInstance(lpProc);
\r
6830 /*---------------------------------------------------------------------------*\
\r
6832 * Ics Interaction console functions
\r
6834 \*---------------------------------------------------------------------------*/
\r
6836 #define HISTORY_SIZE 64
\r
6837 static char *history[HISTORY_SIZE];
\r
6838 int histIn = 0, histP = 0;
\r
6841 SaveInHistory(char *cmd)
\r
6843 if (history[histIn] != NULL) {
\r
6844 free(history[histIn]);
\r
6845 history[histIn] = NULL;
\r
6847 if (*cmd == NULLCHAR) return;
\r
6848 history[histIn] = StrSave(cmd);
\r
6849 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6850 if (history[histIn] != NULL) {
\r
6851 free(history[histIn]);
\r
6852 history[histIn] = NULL;
\r
6858 PrevInHistory(char *cmd)
\r
6861 if (histP == histIn) {
\r
6862 if (history[histIn] != NULL) free(history[histIn]);
\r
6863 history[histIn] = StrSave(cmd);
\r
6865 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6866 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6868 return history[histP];
\r
6874 if (histP == histIn) return NULL;
\r
6875 histP = (histP + 1) % HISTORY_SIZE;
\r
6876 return history[histP];
\r
6880 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6884 hmenu = LoadMenu(hInst, "TextMenu");
\r
6885 h = GetSubMenu(hmenu, 0);
\r
6887 if (strcmp(e->item, "-") == 0) {
\r
6888 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6889 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6890 int flags = MF_STRING, j = 0;
\r
6891 if (e->item[0] == '|') {
\r
6892 flags |= MF_MENUBARBREAK;
\r
6895 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6896 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6904 WNDPROC consoleTextWindowProc;
\r
6907 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6909 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6910 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6914 SetWindowText(hInput, command);
\r
6916 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6918 sel.cpMin = 999999;
\r
6919 sel.cpMax = 999999;
\r
6920 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6925 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6926 if (sel.cpMin == sel.cpMax) {
\r
6927 /* Expand to surrounding word */
\r
6930 tr.chrg.cpMax = sel.cpMin;
\r
6931 tr.chrg.cpMin = --sel.cpMin;
\r
6932 if (sel.cpMin < 0) break;
\r
6933 tr.lpstrText = name;
\r
6934 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6935 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6939 tr.chrg.cpMin = sel.cpMax;
\r
6940 tr.chrg.cpMax = ++sel.cpMax;
\r
6941 tr.lpstrText = name;
\r
6942 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6943 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6946 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6947 MessageBeep(MB_ICONEXCLAMATION);
\r
6951 tr.lpstrText = name;
\r
6952 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6954 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6955 MessageBeep(MB_ICONEXCLAMATION);
\r
6958 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6961 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6962 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6963 SetWindowText(hInput, buf);
\r
6964 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6966 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6967 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6968 SetWindowText(hInput, buf);
\r
6969 sel.cpMin = 999999;
\r
6970 sel.cpMax = 999999;
\r
6971 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6977 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6982 switch (message) {
\r
6984 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6985 if(wParam=='R') return 0;
\r
6988 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6991 sel.cpMin = 999999;
\r
6992 sel.cpMax = 999999;
\r
6993 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6994 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6999 if(wParam != '\022') {
\r
7000 if (wParam == '\t') {
\r
7001 if (GetKeyState(VK_SHIFT) < 0) {
\r
7003 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7004 if (buttonDesc[0].hwnd) {
\r
7005 SetFocus(buttonDesc[0].hwnd);
\r
7007 SetFocus(hwndMain);
\r
7011 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7014 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7015 JAWS_DELETE( SetFocus(hInput); )
\r
7016 SendMessage(hInput, message, wParam, lParam);
\r
7019 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7021 case WM_RBUTTONDOWN:
\r
7022 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7023 /* Move selection here if it was empty */
\r
7025 pt.x = LOWORD(lParam);
\r
7026 pt.y = HIWORD(lParam);
\r
7027 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7028 if (sel.cpMin == sel.cpMax) {
\r
7029 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7030 sel.cpMax = sel.cpMin;
\r
7031 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7033 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7034 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7036 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7037 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7038 if (sel.cpMin == sel.cpMax) {
\r
7039 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7040 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7042 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7043 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7045 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7046 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7047 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7048 MenuPopup(hwnd, pt, hmenu, -1);
\r
7052 case WM_RBUTTONUP:
\r
7053 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7054 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7055 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7059 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7061 return SendMessage(hInput, message, wParam, lParam);
\r
7062 case WM_MBUTTONDOWN:
\r
7063 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7065 switch (LOWORD(wParam)) {
\r
7066 case IDM_QuickPaste:
\r
7068 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7069 if (sel.cpMin == sel.cpMax) {
\r
7070 MessageBeep(MB_ICONEXCLAMATION);
\r
7073 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7074 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7075 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7080 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7083 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7086 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7090 int i = LOWORD(wParam) - IDM_CommandX;
\r
7091 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7092 icsTextMenuEntry[i].command != NULL) {
\r
7093 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7094 icsTextMenuEntry[i].getname,
\r
7095 icsTextMenuEntry[i].immediate);
\r
7103 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7106 WNDPROC consoleInputWindowProc;
\r
7109 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7111 char buf[MSG_SIZ];
\r
7113 static BOOL sendNextChar = FALSE;
\r
7114 static BOOL quoteNextChar = FALSE;
\r
7115 InputSource *is = consoleInputSource;
\r
7119 switch (message) {
\r
7121 if (!appData.localLineEditing || sendNextChar) {
\r
7122 is->buf[0] = (CHAR) wParam;
\r
7124 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7125 sendNextChar = FALSE;
\r
7128 if (quoteNextChar) {
\r
7129 buf[0] = (char) wParam;
\r
7130 buf[1] = NULLCHAR;
\r
7131 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7132 quoteNextChar = FALSE;
\r
7136 case '\r': /* Enter key */
\r
7137 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7138 if (consoleEcho) SaveInHistory(is->buf);
\r
7139 is->buf[is->count++] = '\n';
\r
7140 is->buf[is->count] = NULLCHAR;
\r
7141 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7142 if (consoleEcho) {
\r
7143 ConsoleOutput(is->buf, is->count, TRUE);
\r
7144 } else if (appData.localLineEditing) {
\r
7145 ConsoleOutput("\n", 1, TRUE);
\r
7148 case '\033': /* Escape key */
\r
7149 SetWindowText(hwnd, "");
\r
7150 cf.cbSize = sizeof(CHARFORMAT);
\r
7151 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7152 if (consoleEcho) {
\r
7153 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7155 cf.crTextColor = COLOR_ECHOOFF;
\r
7157 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7158 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7160 case '\t': /* Tab key */
\r
7161 if (GetKeyState(VK_SHIFT) < 0) {
\r
7163 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7166 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7167 if (buttonDesc[0].hwnd) {
\r
7168 SetFocus(buttonDesc[0].hwnd);
\r
7170 SetFocus(hwndMain);
\r
7174 case '\023': /* Ctrl+S */
\r
7175 sendNextChar = TRUE;
\r
7177 case '\021': /* Ctrl+Q */
\r
7178 quoteNextChar = TRUE;
\r
7188 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7189 p = PrevInHistory(buf);
\r
7191 SetWindowText(hwnd, p);
\r
7192 sel.cpMin = 999999;
\r
7193 sel.cpMax = 999999;
\r
7194 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7199 p = NextInHistory();
\r
7201 SetWindowText(hwnd, p);
\r
7202 sel.cpMin = 999999;
\r
7203 sel.cpMax = 999999;
\r
7204 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7210 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7214 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7218 case WM_MBUTTONDOWN:
\r
7219 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7220 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7222 case WM_RBUTTONUP:
\r
7223 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7224 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7225 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7229 hmenu = LoadMenu(hInst, "InputMenu");
\r
7230 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7231 if (sel.cpMin == sel.cpMax) {
\r
7232 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7233 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7235 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7236 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7238 pt.x = LOWORD(lParam);
\r
7239 pt.y = HIWORD(lParam);
\r
7240 MenuPopup(hwnd, pt, hmenu, -1);
\r
7244 switch (LOWORD(wParam)) {
\r
7246 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7248 case IDM_SelectAll:
\r
7250 sel.cpMax = -1; /*999999?*/
\r
7251 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7254 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7257 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7260 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7265 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7268 #define CO_MAX 100000
\r
7269 #define CO_TRIM 1000
\r
7272 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7274 static SnapData sd;
\r
7275 HWND hText, hInput;
\r
7277 static int sizeX, sizeY;
\r
7278 int newSizeX, newSizeY;
\r
7282 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7283 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7285 switch (message) {
\r
7287 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7289 ENLINK *pLink = (ENLINK*)lParam;
\r
7290 if (pLink->msg == WM_LBUTTONUP)
\r
7294 tr.chrg = pLink->chrg;
\r
7295 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7296 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7297 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7298 free(tr.lpstrText);
\r
7302 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7303 hwndConsole = hDlg;
\r
7305 consoleTextWindowProc = (WNDPROC)
\r
7306 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7307 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7308 consoleInputWindowProc = (WNDPROC)
\r
7309 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7310 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7311 Colorize(ColorNormal, TRUE);
\r
7312 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7313 ChangedConsoleFont();
\r
7314 GetClientRect(hDlg, &rect);
\r
7315 sizeX = rect.right;
\r
7316 sizeY = rect.bottom;
\r
7317 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7318 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7319 WINDOWPLACEMENT wp;
\r
7320 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7321 wp.length = sizeof(WINDOWPLACEMENT);
\r
7323 wp.showCmd = SW_SHOW;
\r
7324 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7325 wp.rcNormalPosition.left = wpConsole.x;
\r
7326 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7327 wp.rcNormalPosition.top = wpConsole.y;
\r
7328 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7329 SetWindowPlacement(hDlg, &wp);
\r
7332 // [HGM] Chessknight's change 2004-07-13
\r
7333 else { /* Determine Defaults */
\r
7334 WINDOWPLACEMENT wp;
\r
7335 wpConsole.x = wpMain.width + 1;
\r
7336 wpConsole.y = wpMain.y;
\r
7337 wpConsole.width = screenWidth - wpMain.width;
\r
7338 wpConsole.height = wpMain.height;
\r
7339 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7340 wp.length = sizeof(WINDOWPLACEMENT);
\r
7342 wp.showCmd = SW_SHOW;
\r
7343 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7344 wp.rcNormalPosition.left = wpConsole.x;
\r
7345 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7346 wp.rcNormalPosition.top = wpConsole.y;
\r
7347 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7348 SetWindowPlacement(hDlg, &wp);
\r
7351 // Allow hText to highlight URLs and send notifications on them
\r
7352 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7353 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7354 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7355 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7369 if (IsIconic(hDlg)) break;
\r
7370 newSizeX = LOWORD(lParam);
\r
7371 newSizeY = HIWORD(lParam);
\r
7372 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7373 RECT rectText, rectInput;
\r
7375 int newTextHeight, newTextWidth;
\r
7376 GetWindowRect(hText, &rectText);
\r
7377 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7378 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7379 if (newTextHeight < 0) {
\r
7380 newSizeY += -newTextHeight;
\r
7381 newTextHeight = 0;
\r
7383 SetWindowPos(hText, NULL, 0, 0,
\r
7384 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7385 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7386 pt.x = rectInput.left;
\r
7387 pt.y = rectInput.top + newSizeY - sizeY;
\r
7388 ScreenToClient(hDlg, &pt);
\r
7389 SetWindowPos(hInput, NULL,
\r
7390 pt.x, pt.y, /* needs client coords */
\r
7391 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7392 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7398 case WM_GETMINMAXINFO:
\r
7399 /* Prevent resizing window too small */
\r
7400 mmi = (MINMAXINFO *) lParam;
\r
7401 mmi->ptMinTrackSize.x = 100;
\r
7402 mmi->ptMinTrackSize.y = 100;
\r
7405 /* [AS] Snapping */
\r
7406 case WM_ENTERSIZEMOVE:
\r
7407 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7410 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7413 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7415 case WM_EXITSIZEMOVE:
\r
7416 UpdateICSWidth(hText);
\r
7417 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7420 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7428 if (hwndConsole) return;
\r
7429 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7430 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7435 ConsoleOutput(char* data, int length, int forceVisible)
\r
7440 char buf[CO_MAX+1];
\r
7443 static int delayLF = 0;
\r
7444 CHARRANGE savesel, sel;
\r
7446 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7454 while (length--) {
\r
7462 } else if (*p == '\007') {
\r
7463 MyPlaySound(&sounds[(int)SoundBell]);
\r
7470 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7471 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7472 /* Save current selection */
\r
7473 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7474 exlen = GetWindowTextLength(hText);
\r
7475 /* Find out whether current end of text is visible */
\r
7476 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7477 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7478 /* Trim existing text if it's too long */
\r
7479 if (exlen + (q - buf) > CO_MAX) {
\r
7480 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7483 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7484 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7486 savesel.cpMin -= trim;
\r
7487 savesel.cpMax -= trim;
\r
7488 if (exlen < 0) exlen = 0;
\r
7489 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7490 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7492 /* Append the new text */
\r
7493 sel.cpMin = exlen;
\r
7494 sel.cpMax = exlen;
\r
7495 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7496 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7497 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7498 if (forceVisible || exlen == 0 ||
\r
7499 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7500 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7501 /* Scroll to make new end of text visible if old end of text
\r
7502 was visible or new text is an echo of user typein */
\r
7503 sel.cpMin = 9999999;
\r
7504 sel.cpMax = 9999999;
\r
7505 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7506 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7507 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7508 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7510 if (savesel.cpMax == exlen || forceVisible) {
\r
7511 /* Move insert point to new end of text if it was at the old
\r
7512 end of text or if the new text is an echo of user typein */
\r
7513 sel.cpMin = 9999999;
\r
7514 sel.cpMax = 9999999;
\r
7515 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7517 /* Restore previous selection */
\r
7518 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7520 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7527 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7531 COLORREF oldFg, oldBg;
\r
7535 if(copyNumber > 1)
\r
7536 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7538 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7539 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7540 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7543 rect.right = x + squareSize;
\r
7545 rect.bottom = y + squareSize;
\r
7548 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7549 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7550 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7551 &rect, str, strlen(str), NULL);
\r
7553 (void) SetTextColor(hdc, oldFg);
\r
7554 (void) SetBkColor(hdc, oldBg);
\r
7555 (void) SelectObject(hdc, oldFont);
\r
7559 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7560 RECT *rect, char *color, char *flagFell)
\r
7564 COLORREF oldFg, oldBg;
\r
7567 if (twoBoards && partnerUp) return;
\r
7568 if (appData.clockMode) {
\r
7570 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7572 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7579 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7580 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7582 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7583 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7585 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7589 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7590 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7591 rect, str, strlen(str), NULL);
\r
7592 if(logoHeight > 0 && appData.clockMode) {
\r
7594 str += strlen(color)+2;
\r
7595 r.top = rect->top + logoHeight/2;
\r
7596 r.left = rect->left;
\r
7597 r.right = rect->right;
\r
7598 r.bottom = rect->bottom;
\r
7599 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7600 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7601 &r, str, strlen(str), NULL);
\r
7603 (void) SetTextColor(hdc, oldFg);
\r
7604 (void) SetBkColor(hdc, oldBg);
\r
7605 (void) SelectObject(hdc, oldFont);
\r
7610 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7616 if( count <= 0 ) {
\r
7617 if (appData.debugMode) {
\r
7618 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7621 return ERROR_INVALID_USER_BUFFER;
\r
7624 ResetEvent(ovl->hEvent);
\r
7625 ovl->Offset = ovl->OffsetHigh = 0;
\r
7626 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7630 err = GetLastError();
\r
7631 if (err == ERROR_IO_PENDING) {
\r
7632 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7636 err = GetLastError();
\r
7643 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7648 ResetEvent(ovl->hEvent);
\r
7649 ovl->Offset = ovl->OffsetHigh = 0;
\r
7650 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7654 err = GetLastError();
\r
7655 if (err == ERROR_IO_PENDING) {
\r
7656 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7660 err = GetLastError();
\r
7666 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7667 void CheckForInputBufferFull( InputSource * is )
\r
7669 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7670 /* Look for end of line */
\r
7671 char * p = is->buf;
\r
7673 while( p < is->next && *p != '\n' ) {
\r
7677 if( p >= is->next ) {
\r
7678 if (appData.debugMode) {
\r
7679 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7682 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7683 is->count = (DWORD) -1;
\r
7684 is->next = is->buf;
\r
7690 InputThread(LPVOID arg)
\r
7695 is = (InputSource *) arg;
\r
7696 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7697 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7698 while (is->hThread != NULL) {
\r
7699 is->error = DoReadFile(is->hFile, is->next,
\r
7700 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7701 &is->count, &ovl);
\r
7702 if (is->error == NO_ERROR) {
\r
7703 is->next += is->count;
\r
7705 if (is->error == ERROR_BROKEN_PIPE) {
\r
7706 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7709 is->count = (DWORD) -1;
\r
7710 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7715 CheckForInputBufferFull( is );
\r
7717 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7719 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7721 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7724 CloseHandle(ovl.hEvent);
\r
7725 CloseHandle(is->hFile);
\r
7727 if (appData.debugMode) {
\r
7728 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7735 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7737 NonOvlInputThread(LPVOID arg)
\r
7744 is = (InputSource *) arg;
\r
7745 while (is->hThread != NULL) {
\r
7746 is->error = ReadFile(is->hFile, is->next,
\r
7747 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7748 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7749 if (is->error == NO_ERROR) {
\r
7750 /* Change CRLF to LF */
\r
7751 if (is->next > is->buf) {
\r
7753 i = is->count + 1;
\r
7761 if (prev == '\r' && *p == '\n') {
\r
7773 if (is->error == ERROR_BROKEN_PIPE) {
\r
7774 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7777 is->count = (DWORD) -1;
\r
7781 CheckForInputBufferFull( is );
\r
7783 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7785 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7787 if (is->count < 0) break; /* Quit on error */
\r
7789 CloseHandle(is->hFile);
\r
7794 SocketInputThread(LPVOID arg)
\r
7798 is = (InputSource *) arg;
\r
7799 while (is->hThread != NULL) {
\r
7800 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7801 if ((int)is->count == SOCKET_ERROR) {
\r
7802 is->count = (DWORD) -1;
\r
7803 is->error = WSAGetLastError();
\r
7805 is->error = NO_ERROR;
\r
7806 is->next += is->count;
\r
7807 if (is->count == 0 && is->second == is) {
\r
7808 /* End of file on stderr; quit with no message */
\r
7812 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7814 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7816 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7822 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7826 is = (InputSource *) lParam;
\r
7827 if (is->lineByLine) {
\r
7828 /* Feed in lines one by one */
\r
7829 char *p = is->buf;
\r
7831 while (q < is->next) {
\r
7832 if (*q++ == '\n') {
\r
7833 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7838 /* Move any partial line to the start of the buffer */
\r
7840 while (p < is->next) {
\r
7845 if (is->error != NO_ERROR || is->count == 0) {
\r
7846 /* Notify backend of the error. Note: If there was a partial
\r
7847 line at the end, it is not flushed through. */
\r
7848 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7851 /* Feed in the whole chunk of input at once */
\r
7852 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7853 is->next = is->buf;
\r
7857 /*---------------------------------------------------------------------------*\
\r
7859 * Menu enables. Used when setting various modes.
\r
7861 \*---------------------------------------------------------------------------*/
\r
7869 GreyRevert(Boolean grey)
\r
7870 { // [HGM] vari: for retracting variations in local mode
\r
7871 HMENU hmenu = GetMenu(hwndMain);
\r
7872 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7873 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7877 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7879 while (enab->item > 0) {
\r
7880 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7885 Enables gnuEnables[] = {
\r
7886 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7900 // Needed to switch from ncp to GNU mode on Engine Load
\r
7901 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7902 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7918 Enables icsEnables[] = {
\r
7919 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7929 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7943 Enables zippyEnables[] = {
\r
7944 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7945 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7946 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7952 Enables ncpEnables[] = {
\r
7953 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7963 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7978 Enables trainingOnEnables[] = {
\r
7979 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7987 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7991 Enables trainingOffEnables[] = {
\r
7992 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7993 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7994 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7995 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7996 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7997 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7998 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7999 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8000 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8004 /* These modify either ncpEnables or gnuEnables */
\r
8005 Enables cmailEnables[] = {
\r
8006 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8007 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8008 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8009 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8011 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8016 Enables machineThinkingEnables[] = {
\r
8017 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8018 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8027 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8030 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8036 Enables userThinkingEnables[] = {
\r
8037 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8042 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8043 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8044 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8048 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8050 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8056 /*---------------------------------------------------------------------------*\
\r
8058 * Front-end interface functions exported by XBoard.
\r
8059 * Functions appear in same order as prototypes in frontend.h.
\r
8061 \*---------------------------------------------------------------------------*/
\r
8063 CheckMark(UINT item, int state)
\r
8065 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8071 static UINT prevChecked = 0;
\r
8072 static int prevPausing = 0;
\r
8075 if (pausing != prevPausing) {
\r
8076 prevPausing = pausing;
\r
8077 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8078 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8079 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8082 switch (gameMode) {
\r
8083 case BeginningOfGame:
\r
8084 if (appData.icsActive)
\r
8085 nowChecked = IDM_IcsClient;
\r
8086 else if (appData.noChessProgram)
\r
8087 nowChecked = IDM_EditGame;
\r
8089 nowChecked = IDM_MachineBlack;
\r
8091 case MachinePlaysBlack:
\r
8092 nowChecked = IDM_MachineBlack;
\r
8094 case MachinePlaysWhite:
\r
8095 nowChecked = IDM_MachineWhite;
\r
8097 case TwoMachinesPlay:
\r
8098 nowChecked = IDM_TwoMachines;
\r
8101 nowChecked = IDM_AnalysisMode;
\r
8104 nowChecked = IDM_AnalyzeFile;
\r
8107 nowChecked = IDM_EditGame;
\r
8109 case PlayFromGameFile:
\r
8110 nowChecked = IDM_LoadGame;
\r
8112 case EditPosition:
\r
8113 nowChecked = IDM_EditPosition;
\r
8116 nowChecked = IDM_Training;
\r
8118 case IcsPlayingWhite:
\r
8119 case IcsPlayingBlack:
\r
8120 case IcsObserving:
\r
8122 nowChecked = IDM_IcsClient;
\r
8129 CheckMark(prevChecked, MF_UNCHECKED);
\r
8130 CheckMark(nowChecked, MF_CHECKED);
\r
8131 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8133 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8134 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8135 MF_BYCOMMAND|MF_ENABLED);
\r
8137 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8138 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8141 prevChecked = nowChecked;
\r
8143 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8144 if (appData.icsActive) {
\r
8145 if (appData.icsEngineAnalyze) {
\r
8146 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8148 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8151 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8157 HMENU hmenu = GetMenu(hwndMain);
\r
8158 SetMenuEnables(hmenu, icsEnables);
\r
8159 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8160 MF_BYCOMMAND|MF_ENABLED);
\r
8162 if (appData.zippyPlay) {
\r
8163 SetMenuEnables(hmenu, zippyEnables);
\r
8164 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8165 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8166 MF_BYCOMMAND|MF_ENABLED);
\r
8174 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8180 HMENU hmenu = GetMenu(hwndMain);
\r
8181 SetMenuEnables(hmenu, ncpEnables);
\r
8182 DrawMenuBar(hwndMain);
\r
8188 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8192 SetTrainingModeOn()
\r
8195 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8196 for (i = 0; i < N_BUTTONS; i++) {
\r
8197 if (buttonDesc[i].hwnd != NULL)
\r
8198 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8203 VOID SetTrainingModeOff()
\r
8206 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8207 for (i = 0; i < N_BUTTONS; i++) {
\r
8208 if (buttonDesc[i].hwnd != NULL)
\r
8209 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8215 SetUserThinkingEnables()
\r
8217 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8221 SetMachineThinkingEnables()
\r
8223 HMENU hMenu = GetMenu(hwndMain);
\r
8224 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8226 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8228 if (gameMode == MachinePlaysBlack) {
\r
8229 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8230 } else if (gameMode == MachinePlaysWhite) {
\r
8231 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8232 } else if (gameMode == TwoMachinesPlay) {
\r
8233 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8239 DisplayTitle(char *str)
\r
8241 char title[MSG_SIZ], *host;
\r
8242 if (str[0] != NULLCHAR) {
\r
8243 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8244 } else if (appData.icsActive) {
\r
8245 if (appData.icsCommPort[0] != NULLCHAR)
\r
8248 host = appData.icsHost;
\r
8249 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8250 } else if (appData.noChessProgram) {
\r
8251 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8253 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8254 strcat(title, ": ");
\r
8255 strcat(title, first.tidy);
\r
8257 SetWindowText(hwndMain, title);
\r
8262 DisplayMessage(char *str1, char *str2)
\r
8266 int remain = MESSAGE_TEXT_MAX - 1;
\r
8269 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8270 messageText[0] = NULLCHAR;
\r
8272 len = strlen(str1);
\r
8273 if (len > remain) len = remain;
\r
8274 strncpy(messageText, str1, len);
\r
8275 messageText[len] = NULLCHAR;
\r
8278 if (*str2 && remain >= 2) {
\r
8280 strcat(messageText, " ");
\r
8283 len = strlen(str2);
\r
8284 if (len > remain) len = remain;
\r
8285 strncat(messageText, str2, len);
\r
8287 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8288 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8290 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8294 hdc = GetDC(hwndMain);
\r
8295 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8296 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8297 &messageRect, messageText, strlen(messageText), NULL);
\r
8298 (void) SelectObject(hdc, oldFont);
\r
8299 (void) ReleaseDC(hwndMain, hdc);
\r
8303 DisplayError(char *str, int error)
\r
8305 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8309 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8311 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8312 NULL, error, LANG_NEUTRAL,
\r
8313 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8315 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8317 ErrorMap *em = errmap;
\r
8318 while (em->err != 0 && em->err != error) em++;
\r
8319 if (em->err != 0) {
\r
8320 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8322 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8327 ErrorPopUp(_("Error"), buf);
\r
8332 DisplayMoveError(char *str)
\r
8334 fromX = fromY = -1;
\r
8335 ClearHighlights();
\r
8336 DrawPosition(FALSE, NULL);
\r
8337 if (appData.popupMoveErrors) {
\r
8338 ErrorPopUp(_("Error"), str);
\r
8340 DisplayMessage(str, "");
\r
8341 moveErrorMessageUp = TRUE;
\r
8346 DisplayFatalError(char *str, int error, int exitStatus)
\r
8348 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8350 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8353 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8354 NULL, error, LANG_NEUTRAL,
\r
8355 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8357 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8359 ErrorMap *em = errmap;
\r
8360 while (em->err != 0 && em->err != error) em++;
\r
8361 if (em->err != 0) {
\r
8362 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8364 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8369 if (appData.debugMode) {
\r
8370 fprintf(debugFP, "%s: %s\n", label, str);
\r
8372 if (appData.popupExitMessage) {
\r
8373 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8374 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8376 ExitEvent(exitStatus);
\r
8381 DisplayInformation(char *str)
\r
8383 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8388 DisplayNote(char *str)
\r
8390 ErrorPopUp(_("Note"), str);
\r
8395 char *title, *question, *replyPrefix;
\r
8400 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8402 static QuestionParams *qp;
\r
8403 char reply[MSG_SIZ];
\r
8406 switch (message) {
\r
8407 case WM_INITDIALOG:
\r
8408 qp = (QuestionParams *) lParam;
\r
8409 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8410 Translate(hDlg, DLG_Question);
\r
8411 SetWindowText(hDlg, qp->title);
\r
8412 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8413 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8417 switch (LOWORD(wParam)) {
\r
8419 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8420 if (*reply) strcat(reply, " ");
\r
8421 len = strlen(reply);
\r
8422 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8423 strcat(reply, "\n");
\r
8424 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8425 EndDialog(hDlg, TRUE);
\r
8426 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8429 EndDialog(hDlg, FALSE);
\r
8440 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8442 QuestionParams qp;
\r
8446 qp.question = question;
\r
8447 qp.replyPrefix = replyPrefix;
\r
8449 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8450 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8451 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8452 FreeProcInstance(lpProc);
\r
8455 /* [AS] Pick FRC position */
\r
8456 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8458 static int * lpIndexFRC;
\r
8464 case WM_INITDIALOG:
\r
8465 lpIndexFRC = (int *) lParam;
\r
8467 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8468 Translate(hDlg, DLG_NewGameFRC);
\r
8470 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8471 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8472 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8473 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8478 switch( LOWORD(wParam) ) {
\r
8480 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8481 EndDialog( hDlg, 0 );
\r
8482 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8485 EndDialog( hDlg, 1 );
\r
8487 case IDC_NFG_Edit:
\r
8488 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8489 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8491 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8494 case IDC_NFG_Random:
\r
8495 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8496 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8509 int index = appData.defaultFrcPosition;
\r
8510 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8512 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8514 if( result == 0 ) {
\r
8515 appData.defaultFrcPosition = index;
\r
8521 /* [AS] Game list options. Refactored by HGM */
\r
8523 HWND gameListOptionsDialog;
\r
8525 // low-level front-end: clear text edit / list widget
\r
8529 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8532 // low-level front-end: clear text edit / list widget
\r
8534 GLT_DeSelectList()
\r
8536 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8539 // low-level front-end: append line to text edit / list widget
\r
8541 GLT_AddToList( char *name )
\r
8544 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8548 // low-level front-end: get line from text edit / list widget
\r
8550 GLT_GetFromList( int index, char *name )
\r
8553 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8559 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8561 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8562 int idx2 = idx1 + delta;
\r
8563 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8565 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8568 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8569 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8570 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8571 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8575 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8579 case WM_INITDIALOG:
\r
8580 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8582 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8583 Translate(hDlg, DLG_GameListOptions);
\r
8585 /* Initialize list */
\r
8586 GLT_TagsToList( lpUserGLT );
\r
8588 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8593 switch( LOWORD(wParam) ) {
\r
8596 EndDialog( hDlg, 0 );
\r
8599 EndDialog( hDlg, 1 );
\r
8602 case IDC_GLT_Default:
\r
8603 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8606 case IDC_GLT_Restore:
\r
8607 GLT_TagsToList( appData.gameListTags );
\r
8611 GLT_MoveSelection( hDlg, -1 );
\r
8614 case IDC_GLT_Down:
\r
8615 GLT_MoveSelection( hDlg, +1 );
\r
8625 int GameListOptions()
\r
8628 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8630 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8632 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8634 if( result == 0 ) {
\r
8635 /* [AS] Memory leak here! */
\r
8636 appData.gameListTags = strdup( lpUserGLT );
\r
8643 DisplayIcsInteractionTitle(char *str)
\r
8645 char consoleTitle[MSG_SIZ];
\r
8647 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8648 SetWindowText(hwndConsole, consoleTitle);
\r
8650 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8651 char buf[MSG_SIZ], *p = buf, *q;
\r
8652 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8654 q = strchr(p, ';');
\r
8656 if(*p) ChatPopUp(p);
\r
8660 SetActiveWindow(hwndMain);
\r
8664 DrawPosition(int fullRedraw, Board board)
\r
8666 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8669 void NotifyFrontendLogin()
\r
8672 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8678 fromX = fromY = -1;
\r
8679 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8680 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8681 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8682 dragInfo.lastpos = dragInfo.pos;
\r
8683 dragInfo.start.x = dragInfo.start.y = -1;
\r
8684 dragInfo.from = dragInfo.start;
\r
8686 DrawPosition(TRUE, NULL);
\r
8693 CommentPopUp(char *title, char *str)
\r
8695 HWND hwnd = GetActiveWindow();
\r
8696 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8698 SetActiveWindow(hwnd);
\r
8702 CommentPopDown(void)
\r
8704 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8705 if (commentDialog) {
\r
8706 ShowWindow(commentDialog, SW_HIDE);
\r
8708 commentUp = FALSE;
\r
8712 EditCommentPopUp(int index, char *title, char *str)
\r
8714 EitherCommentPopUp(index, title, str, TRUE);
\r
8721 MyPlaySound(&sounds[(int)SoundMove]);
\r
8724 VOID PlayIcsWinSound()
\r
8726 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8729 VOID PlayIcsLossSound()
\r
8731 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8734 VOID PlayIcsDrawSound()
\r
8736 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8739 VOID PlayIcsUnfinishedSound()
\r
8741 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8747 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8753 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8761 consoleEcho = TRUE;
\r
8762 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8763 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8764 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8773 consoleEcho = FALSE;
\r
8774 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8775 /* This works OK: set text and background both to the same color */
\r
8777 cf.crTextColor = COLOR_ECHOOFF;
\r
8778 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8779 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8782 /* No Raw()...? */
\r
8784 void Colorize(ColorClass cc, int continuation)
\r
8786 currentColorClass = cc;
\r
8787 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8788 consoleCF.crTextColor = textAttribs[cc].color;
\r
8789 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8790 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8796 static char buf[MSG_SIZ];
\r
8797 DWORD bufsiz = MSG_SIZ;
\r
8799 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8800 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8802 if (!GetUserName(buf, &bufsiz)) {
\r
8803 /*DisplayError("Error getting user name", GetLastError());*/
\r
8804 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8812 static char buf[MSG_SIZ];
\r
8813 DWORD bufsiz = MSG_SIZ;
\r
8815 if (!GetComputerName(buf, &bufsiz)) {
\r
8816 /*DisplayError("Error getting host name", GetLastError());*/
\r
8817 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8824 ClockTimerRunning()
\r
8826 return clockTimerEvent != 0;
\r
8832 if (clockTimerEvent == 0) return FALSE;
\r
8833 KillTimer(hwndMain, clockTimerEvent);
\r
8834 clockTimerEvent = 0;
\r
8839 StartClockTimer(long millisec)
\r
8841 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8842 (UINT) millisec, NULL);
\r
8846 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8849 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8851 if(appData.noGUI) return;
\r
8852 hdc = GetDC(hwndMain);
\r
8853 if (!IsIconic(hwndMain)) {
\r
8854 DisplayAClock(hdc, timeRemaining, highlight,
\r
8855 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8857 if (highlight && iconCurrent == iconBlack) {
\r
8858 iconCurrent = iconWhite;
\r
8859 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8860 if (IsIconic(hwndMain)) {
\r
8861 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8864 (void) ReleaseDC(hwndMain, hdc);
\r
8866 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8870 DisplayBlackClock(long timeRemaining, int highlight)
\r
8873 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8875 if(appData.noGUI) return;
\r
8876 hdc = GetDC(hwndMain);
\r
8877 if (!IsIconic(hwndMain)) {
\r
8878 DisplayAClock(hdc, timeRemaining, highlight,
\r
8879 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8881 if (highlight && iconCurrent == iconWhite) {
\r
8882 iconCurrent = iconBlack;
\r
8883 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8884 if (IsIconic(hwndMain)) {
\r
8885 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8888 (void) ReleaseDC(hwndMain, hdc);
\r
8890 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8895 LoadGameTimerRunning()
\r
8897 return loadGameTimerEvent != 0;
\r
8901 StopLoadGameTimer()
\r
8903 if (loadGameTimerEvent == 0) return FALSE;
\r
8904 KillTimer(hwndMain, loadGameTimerEvent);
\r
8905 loadGameTimerEvent = 0;
\r
8910 StartLoadGameTimer(long millisec)
\r
8912 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8913 (UINT) millisec, NULL);
\r
8921 char fileTitle[MSG_SIZ];
\r
8923 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8924 f = OpenFileDialog(hwndMain, "a", defName,
\r
8925 appData.oldSaveStyle ? "gam" : "pgn",
\r
8927 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8929 SaveGame(f, 0, "");
\r
8936 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8938 if (delayedTimerEvent != 0) {
\r
8939 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8940 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8942 KillTimer(hwndMain, delayedTimerEvent);
\r
8943 delayedTimerEvent = 0;
\r
8944 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8945 delayedTimerCallback();
\r
8947 delayedTimerCallback = cb;
\r
8948 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8949 (UINT) millisec, NULL);
\r
8952 DelayedEventCallback
\r
8955 if (delayedTimerEvent) {
\r
8956 return delayedTimerCallback;
\r
8963 CancelDelayedEvent()
\r
8965 if (delayedTimerEvent) {
\r
8966 KillTimer(hwndMain, delayedTimerEvent);
\r
8967 delayedTimerEvent = 0;
\r
8971 DWORD GetWin32Priority(int nice)
\r
8972 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8974 REALTIME_PRIORITY_CLASS 0x00000100
\r
8975 HIGH_PRIORITY_CLASS 0x00000080
\r
8976 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8977 NORMAL_PRIORITY_CLASS 0x00000020
\r
8978 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8979 IDLE_PRIORITY_CLASS 0x00000040
\r
8981 if (nice < -15) return 0x00000080;
\r
8982 if (nice < 0) return 0x00008000;
\r
8983 if (nice == 0) return 0x00000020;
\r
8984 if (nice < 15) return 0x00004000;
\r
8985 return 0x00000040;
\r
8988 void RunCommand(char *cmdLine)
\r
8990 /* Now create the child process. */
\r
8991 STARTUPINFO siStartInfo;
\r
8992 PROCESS_INFORMATION piProcInfo;
\r
8994 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8995 siStartInfo.lpReserved = NULL;
\r
8996 siStartInfo.lpDesktop = NULL;
\r
8997 siStartInfo.lpTitle = NULL;
\r
8998 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8999 siStartInfo.cbReserved2 = 0;
\r
9000 siStartInfo.lpReserved2 = NULL;
\r
9001 siStartInfo.hStdInput = NULL;
\r
9002 siStartInfo.hStdOutput = NULL;
\r
9003 siStartInfo.hStdError = NULL;
\r
9005 CreateProcess(NULL,
\r
9006 cmdLine, /* command line */
\r
9007 NULL, /* process security attributes */
\r
9008 NULL, /* primary thread security attrs */
\r
9009 TRUE, /* handles are inherited */
\r
9010 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9011 NULL, /* use parent's environment */
\r
9013 &siStartInfo, /* STARTUPINFO pointer */
\r
9014 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9016 CloseHandle(piProcInfo.hThread);
\r
9019 /* Start a child process running the given program.
\r
9020 The process's standard output can be read from "from", and its
\r
9021 standard input can be written to "to".
\r
9022 Exit with fatal error if anything goes wrong.
\r
9023 Returns an opaque pointer that can be used to destroy the process
\r
9027 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9029 #define BUFSIZE 4096
\r
9031 HANDLE hChildStdinRd, hChildStdinWr,
\r
9032 hChildStdoutRd, hChildStdoutWr;
\r
9033 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9034 SECURITY_ATTRIBUTES saAttr;
\r
9036 PROCESS_INFORMATION piProcInfo;
\r
9037 STARTUPINFO siStartInfo;
\r
9039 char buf[MSG_SIZ];
\r
9042 if (appData.debugMode) {
\r
9043 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9048 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9049 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9050 saAttr.bInheritHandle = TRUE;
\r
9051 saAttr.lpSecurityDescriptor = NULL;
\r
9054 * The steps for redirecting child's STDOUT:
\r
9055 * 1. Create anonymous pipe to be STDOUT for child.
\r
9056 * 2. Create a noninheritable duplicate of read handle,
\r
9057 * and close the inheritable read handle.
\r
9060 /* Create a pipe for the child's STDOUT. */
\r
9061 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9062 return GetLastError();
\r
9065 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9066 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9067 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9068 FALSE, /* not inherited */
\r
9069 DUPLICATE_SAME_ACCESS);
\r
9071 return GetLastError();
\r
9073 CloseHandle(hChildStdoutRd);
\r
9076 * The steps for redirecting child's STDIN:
\r
9077 * 1. Create anonymous pipe to be STDIN for child.
\r
9078 * 2. Create a noninheritable duplicate of write handle,
\r
9079 * and close the inheritable write handle.
\r
9082 /* Create a pipe for the child's STDIN. */
\r
9083 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9084 return GetLastError();
\r
9087 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9088 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9089 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9090 FALSE, /* not inherited */
\r
9091 DUPLICATE_SAME_ACCESS);
\r
9093 return GetLastError();
\r
9095 CloseHandle(hChildStdinWr);
\r
9097 /* Arrange to (1) look in dir for the child .exe file, and
\r
9098 * (2) have dir be the child's working directory. Interpret
\r
9099 * dir relative to the directory WinBoard loaded from. */
\r
9100 GetCurrentDirectory(MSG_SIZ, buf);
\r
9101 SetCurrentDirectory(installDir);
\r
9102 SetCurrentDirectory(dir);
\r
9104 /* Now create the child process. */
\r
9106 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9107 siStartInfo.lpReserved = NULL;
\r
9108 siStartInfo.lpDesktop = NULL;
\r
9109 siStartInfo.lpTitle = NULL;
\r
9110 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9111 siStartInfo.cbReserved2 = 0;
\r
9112 siStartInfo.lpReserved2 = NULL;
\r
9113 siStartInfo.hStdInput = hChildStdinRd;
\r
9114 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9115 siStartInfo.hStdError = hChildStdoutWr;
\r
9117 fSuccess = CreateProcess(NULL,
\r
9118 cmdLine, /* command line */
\r
9119 NULL, /* process security attributes */
\r
9120 NULL, /* primary thread security attrs */
\r
9121 TRUE, /* handles are inherited */
\r
9122 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9123 NULL, /* use parent's environment */
\r
9125 &siStartInfo, /* STARTUPINFO pointer */
\r
9126 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9128 err = GetLastError();
\r
9129 SetCurrentDirectory(buf); /* return to prev directory */
\r
9134 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9135 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9136 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9139 /* Close the handles we don't need in the parent */
\r
9140 CloseHandle(piProcInfo.hThread);
\r
9141 CloseHandle(hChildStdinRd);
\r
9142 CloseHandle(hChildStdoutWr);
\r
9144 /* Prepare return value */
\r
9145 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9146 cp->kind = CPReal;
\r
9147 cp->hProcess = piProcInfo.hProcess;
\r
9148 cp->pid = piProcInfo.dwProcessId;
\r
9149 cp->hFrom = hChildStdoutRdDup;
\r
9150 cp->hTo = hChildStdinWrDup;
\r
9152 *pr = (void *) cp;
\r
9154 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9155 2000 where engines sometimes don't see the initial command(s)
\r
9156 from WinBoard and hang. I don't understand how that can happen,
\r
9157 but the Sleep is harmless, so I've put it in. Others have also
\r
9158 reported what may be the same problem, so hopefully this will fix
\r
9159 it for them too. */
\r
9167 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9169 ChildProc *cp; int result;
\r
9171 cp = (ChildProc *) pr;
\r
9172 if (cp == NULL) return;
\r
9174 switch (cp->kind) {
\r
9176 /* TerminateProcess is considered harmful, so... */
\r
9177 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9178 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9179 /* The following doesn't work because the chess program
\r
9180 doesn't "have the same console" as WinBoard. Maybe
\r
9181 we could arrange for this even though neither WinBoard
\r
9182 nor the chess program uses a console for stdio? */
\r
9183 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9185 /* [AS] Special termination modes for misbehaving programs... */
\r
9186 if( signal == 9 ) {
\r
9187 result = TerminateProcess( cp->hProcess, 0 );
\r
9189 if ( appData.debugMode) {
\r
9190 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9193 else if( signal == 10 ) {
\r
9194 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9196 if( dw != WAIT_OBJECT_0 ) {
\r
9197 result = TerminateProcess( cp->hProcess, 0 );
\r
9199 if ( appData.debugMode) {
\r
9200 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9206 CloseHandle(cp->hProcess);
\r
9210 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9214 closesocket(cp->sock);
\r
9219 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9220 closesocket(cp->sock);
\r
9221 closesocket(cp->sock2);
\r
9229 InterruptChildProcess(ProcRef pr)
\r
9233 cp = (ChildProc *) pr;
\r
9234 if (cp == NULL) return;
\r
9235 switch (cp->kind) {
\r
9237 /* The following doesn't work because the chess program
\r
9238 doesn't "have the same console" as WinBoard. Maybe
\r
9239 we could arrange for this even though neither WinBoard
\r
9240 nor the chess program uses a console for stdio */
\r
9241 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9246 /* Can't interrupt */
\r
9250 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9257 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9259 char cmdLine[MSG_SIZ];
\r
9261 if (port[0] == NULLCHAR) {
\r
9262 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9264 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9266 return StartChildProcess(cmdLine, "", pr);
\r
9270 /* Code to open TCP sockets */
\r
9273 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9279 struct sockaddr_in sa, mysa;
\r
9280 struct hostent FAR *hp;
\r
9281 unsigned short uport;
\r
9282 WORD wVersionRequested;
\r
9285 /* Initialize socket DLL */
\r
9286 wVersionRequested = MAKEWORD(1, 1);
\r
9287 err = WSAStartup(wVersionRequested, &wsaData);
\r
9288 if (err != 0) return err;
\r
9291 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9292 err = WSAGetLastError();
\r
9297 /* Bind local address using (mostly) don't-care values.
\r
9299 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9300 mysa.sin_family = AF_INET;
\r
9301 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9302 uport = (unsigned short) 0;
\r
9303 mysa.sin_port = htons(uport);
\r
9304 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9305 == SOCKET_ERROR) {
\r
9306 err = WSAGetLastError();
\r
9311 /* Resolve remote host name */
\r
9312 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9313 if (!(hp = gethostbyname(host))) {
\r
9314 unsigned int b0, b1, b2, b3;
\r
9316 err = WSAGetLastError();
\r
9318 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9319 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9320 hp->h_addrtype = AF_INET;
\r
9322 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9323 hp->h_addr_list[0] = (char *) malloc(4);
\r
9324 hp->h_addr_list[0][0] = (char) b0;
\r
9325 hp->h_addr_list[0][1] = (char) b1;
\r
9326 hp->h_addr_list[0][2] = (char) b2;
\r
9327 hp->h_addr_list[0][3] = (char) b3;
\r
9333 sa.sin_family = hp->h_addrtype;
\r
9334 uport = (unsigned short) atoi(port);
\r
9335 sa.sin_port = htons(uport);
\r
9336 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9338 /* Make connection */
\r
9339 if (connect(s, (struct sockaddr *) &sa,
\r
9340 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9341 err = WSAGetLastError();
\r
9346 /* Prepare return value */
\r
9347 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9348 cp->kind = CPSock;
\r
9350 *pr = (ProcRef *) cp;
\r
9356 OpenCommPort(char *name, ProcRef *pr)
\r
9361 char fullname[MSG_SIZ];
\r
9363 if (*name != '\\')
\r
9364 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9366 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9368 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9369 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9370 if (h == (HANDLE) -1) {
\r
9371 return GetLastError();
\r
9375 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9377 /* Accumulate characters until a 100ms pause, then parse */
\r
9378 ct.ReadIntervalTimeout = 100;
\r
9379 ct.ReadTotalTimeoutMultiplier = 0;
\r
9380 ct.ReadTotalTimeoutConstant = 0;
\r
9381 ct.WriteTotalTimeoutMultiplier = 0;
\r
9382 ct.WriteTotalTimeoutConstant = 0;
\r
9383 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9385 /* Prepare return value */
\r
9386 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9387 cp->kind = CPComm;
\r
9390 *pr = (ProcRef *) cp;
\r
9396 OpenLoopback(ProcRef *pr)
\r
9398 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9404 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9409 struct sockaddr_in sa, mysa;
\r
9410 struct hostent FAR *hp;
\r
9411 unsigned short uport;
\r
9412 WORD wVersionRequested;
\r
9415 char stderrPortStr[MSG_SIZ];
\r
9417 /* Initialize socket DLL */
\r
9418 wVersionRequested = MAKEWORD(1, 1);
\r
9419 err = WSAStartup(wVersionRequested, &wsaData);
\r
9420 if (err != 0) return err;
\r
9422 /* Resolve remote host name */
\r
9423 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9424 if (!(hp = gethostbyname(host))) {
\r
9425 unsigned int b0, b1, b2, b3;
\r
9427 err = WSAGetLastError();
\r
9429 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9430 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9431 hp->h_addrtype = AF_INET;
\r
9433 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9434 hp->h_addr_list[0] = (char *) malloc(4);
\r
9435 hp->h_addr_list[0][0] = (char) b0;
\r
9436 hp->h_addr_list[0][1] = (char) b1;
\r
9437 hp->h_addr_list[0][2] = (char) b2;
\r
9438 hp->h_addr_list[0][3] = (char) b3;
\r
9444 sa.sin_family = hp->h_addrtype;
\r
9445 uport = (unsigned short) 514;
\r
9446 sa.sin_port = htons(uport);
\r
9447 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9449 /* Bind local socket to unused "privileged" port address
\r
9451 s = INVALID_SOCKET;
\r
9452 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9453 mysa.sin_family = AF_INET;
\r
9454 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9455 for (fromPort = 1023;; fromPort--) {
\r
9456 if (fromPort < 0) {
\r
9458 return WSAEADDRINUSE;
\r
9460 if (s == INVALID_SOCKET) {
\r
9461 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9462 err = WSAGetLastError();
\r
9467 uport = (unsigned short) fromPort;
\r
9468 mysa.sin_port = htons(uport);
\r
9469 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9470 == SOCKET_ERROR) {
\r
9471 err = WSAGetLastError();
\r
9472 if (err == WSAEADDRINUSE) continue;
\r
9476 if (connect(s, (struct sockaddr *) &sa,
\r
9477 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9478 err = WSAGetLastError();
\r
9479 if (err == WSAEADDRINUSE) {
\r
9490 /* Bind stderr local socket to unused "privileged" port address
\r
9492 s2 = INVALID_SOCKET;
\r
9493 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9494 mysa.sin_family = AF_INET;
\r
9495 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9496 for (fromPort = 1023;; fromPort--) {
\r
9497 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9498 if (fromPort < 0) {
\r
9499 (void) closesocket(s);
\r
9501 return WSAEADDRINUSE;
\r
9503 if (s2 == INVALID_SOCKET) {
\r
9504 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9505 err = WSAGetLastError();
\r
9511 uport = (unsigned short) fromPort;
\r
9512 mysa.sin_port = htons(uport);
\r
9513 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9514 == SOCKET_ERROR) {
\r
9515 err = WSAGetLastError();
\r
9516 if (err == WSAEADDRINUSE) continue;
\r
9517 (void) closesocket(s);
\r
9521 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9522 err = WSAGetLastError();
\r
9523 if (err == WSAEADDRINUSE) {
\r
9525 s2 = INVALID_SOCKET;
\r
9528 (void) closesocket(s);
\r
9529 (void) closesocket(s2);
\r
9535 prevStderrPort = fromPort; // remember port used
\r
9536 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9538 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9539 err = WSAGetLastError();
\r
9540 (void) closesocket(s);
\r
9541 (void) closesocket(s2);
\r
9546 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9547 err = WSAGetLastError();
\r
9548 (void) closesocket(s);
\r
9549 (void) closesocket(s2);
\r
9553 if (*user == NULLCHAR) user = UserName();
\r
9554 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9555 err = WSAGetLastError();
\r
9556 (void) closesocket(s);
\r
9557 (void) closesocket(s2);
\r
9561 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9562 err = WSAGetLastError();
\r
9563 (void) closesocket(s);
\r
9564 (void) closesocket(s2);
\r
9569 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9570 err = WSAGetLastError();
\r
9571 (void) closesocket(s);
\r
9572 (void) closesocket(s2);
\r
9576 (void) closesocket(s2); /* Stop listening */
\r
9578 /* Prepare return value */
\r
9579 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9580 cp->kind = CPRcmd;
\r
9583 *pr = (ProcRef *) cp;
\r
9590 AddInputSource(ProcRef pr, int lineByLine,
\r
9591 InputCallback func, VOIDSTAR closure)
\r
9593 InputSource *is, *is2 = NULL;
\r
9594 ChildProc *cp = (ChildProc *) pr;
\r
9596 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9597 is->lineByLine = lineByLine;
\r
9599 is->closure = closure;
\r
9600 is->second = NULL;
\r
9601 is->next = is->buf;
\r
9602 if (pr == NoProc) {
\r
9603 is->kind = CPReal;
\r
9604 consoleInputSource = is;
\r
9606 is->kind = cp->kind;
\r
9608 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9609 we create all threads suspended so that the is->hThread variable can be
\r
9610 safely assigned, then let the threads start with ResumeThread.
\r
9612 switch (cp->kind) {
\r
9614 is->hFile = cp->hFrom;
\r
9615 cp->hFrom = NULL; /* now owned by InputThread */
\r
9617 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9618 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9622 is->hFile = cp->hFrom;
\r
9623 cp->hFrom = NULL; /* now owned by InputThread */
\r
9625 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9626 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9630 is->sock = cp->sock;
\r
9632 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9633 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9637 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9639 is->sock = cp->sock;
\r
9641 is2->sock = cp->sock2;
\r
9642 is2->second = is2;
\r
9644 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9645 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9647 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9648 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9652 if( is->hThread != NULL ) {
\r
9653 ResumeThread( is->hThread );
\r
9656 if( is2 != NULL && is2->hThread != NULL ) {
\r
9657 ResumeThread( is2->hThread );
\r
9661 return (InputSourceRef) is;
\r
9665 RemoveInputSource(InputSourceRef isr)
\r
9669 is = (InputSource *) isr;
\r
9670 is->hThread = NULL; /* tell thread to stop */
\r
9671 CloseHandle(is->hThread);
\r
9672 if (is->second != NULL) {
\r
9673 is->second->hThread = NULL;
\r
9674 CloseHandle(is->second->hThread);
\r
9678 int no_wrap(char *message, int count)
\r
9680 ConsoleOutput(message, count, FALSE);
\r
9685 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9688 int outCount = SOCKET_ERROR;
\r
9689 ChildProc *cp = (ChildProc *) pr;
\r
9690 static OVERLAPPED ovl;
\r
9691 static int line = 0;
\r
9695 if (appData.noJoin || !appData.useInternalWrap)
\r
9696 return no_wrap(message, count);
\r
9699 int width = get_term_width();
\r
9700 int len = wrap(NULL, message, count, width, &line);
\r
9701 char *msg = malloc(len);
\r
9705 return no_wrap(message, count);
\r
9708 dbgchk = wrap(msg, message, count, width, &line);
\r
9709 if (dbgchk != len && appData.debugMode)
\r
9710 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9711 ConsoleOutput(msg, len, FALSE);
\r
9718 if (ovl.hEvent == NULL) {
\r
9719 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9721 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9723 switch (cp->kind) {
\r
9726 outCount = send(cp->sock, message, count, 0);
\r
9727 if (outCount == SOCKET_ERROR) {
\r
9728 *outError = WSAGetLastError();
\r
9730 *outError = NO_ERROR;
\r
9735 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9736 &dOutCount, NULL)) {
\r
9737 *outError = NO_ERROR;
\r
9738 outCount = (int) dOutCount;
\r
9740 *outError = GetLastError();
\r
9745 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9746 &dOutCount, &ovl);
\r
9747 if (*outError == NO_ERROR) {
\r
9748 outCount = (int) dOutCount;
\r
9758 if(n != 0) Sleep(n);
\r
9762 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9765 /* Ignore delay, not implemented for WinBoard */
\r
9766 return OutputToProcess(pr, message, count, outError);
\r
9771 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9772 char *buf, int count, int error)
\r
9774 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9777 /* see wgamelist.c for Game List functions */
\r
9778 /* see wedittags.c for Edit Tags functions */
\r
9785 char buf[MSG_SIZ];
\r
9788 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9789 f = fopen(buf, "r");
\r
9791 ProcessICSInitScript(f);
\r
9801 StartAnalysisClock()
\r
9803 if (analysisTimerEvent) return;
\r
9804 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9805 (UINT) 2000, NULL);
\r
9809 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9811 highlightInfo.sq[0].x = fromX;
\r
9812 highlightInfo.sq[0].y = fromY;
\r
9813 highlightInfo.sq[1].x = toX;
\r
9814 highlightInfo.sq[1].y = toY;
\r
9820 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9821 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9825 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9827 premoveHighlightInfo.sq[0].x = fromX;
\r
9828 premoveHighlightInfo.sq[0].y = fromY;
\r
9829 premoveHighlightInfo.sq[1].x = toX;
\r
9830 premoveHighlightInfo.sq[1].y = toY;
\r
9834 ClearPremoveHighlights()
\r
9836 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9837 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9841 ShutDownFrontEnd()
\r
9843 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9844 DeleteClipboardTempFiles();
\r
9850 if (IsIconic(hwndMain))
\r
9851 ShowWindow(hwndMain, SW_RESTORE);
\r
9853 SetActiveWindow(hwndMain);
\r
9857 * Prototypes for animation support routines
\r
9859 static void ScreenSquare(int column, int row, POINT * pt);
\r
9860 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9861 POINT frames[], int * nFrames);
\r
9867 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9868 { // [HGM] atomic: animate blast wave
\r
9871 explodeInfo.fromX = fromX;
\r
9872 explodeInfo.fromY = fromY;
\r
9873 explodeInfo.toX = toX;
\r
9874 explodeInfo.toY = toY;
\r
9875 for(i=1; i<4*kFactor; i++) {
\r
9876 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9877 DrawPosition(FALSE, board);
\r
9878 Sleep(appData.animSpeed);
\r
9880 explodeInfo.radius = 0;
\r
9881 DrawPosition(TRUE, board);
\r
9885 AnimateMove(board, fromX, fromY, toX, toY)
\r
9892 ChessSquare piece;
\r
9893 POINT start, finish, mid;
\r
9894 POINT frames[kFactor * 2 + 1];
\r
9897 if (!appData.animate) return;
\r
9898 if (doingSizing) return;
\r
9899 if (fromY < 0 || fromX < 0) return;
\r
9900 piece = board[fromY][fromX];
\r
9901 if (piece >= EmptySquare) return;
\r
9903 ScreenSquare(fromX, fromY, &start);
\r
9904 ScreenSquare(toX, toY, &finish);
\r
9906 /* All moves except knight jumps move in straight line */
\r
9907 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9908 mid.x = start.x + (finish.x - start.x) / 2;
\r
9909 mid.y = start.y + (finish.y - start.y) / 2;
\r
9911 /* Knight: make straight movement then diagonal */
\r
9912 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9913 mid.x = start.x + (finish.x - start.x) / 2;
\r
9917 mid.y = start.y + (finish.y - start.y) / 2;
\r
9921 /* Don't use as many frames for very short moves */
\r
9922 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9923 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9925 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9927 animInfo.from.x = fromX;
\r
9928 animInfo.from.y = fromY;
\r
9929 animInfo.to.x = toX;
\r
9930 animInfo.to.y = toY;
\r
9931 animInfo.lastpos = start;
\r
9932 animInfo.piece = piece;
\r
9933 for (n = 0; n < nFrames; n++) {
\r
9934 animInfo.pos = frames[n];
\r
9935 DrawPosition(FALSE, NULL);
\r
9936 animInfo.lastpos = animInfo.pos;
\r
9937 Sleep(appData.animSpeed);
\r
9939 animInfo.pos = finish;
\r
9940 DrawPosition(FALSE, NULL);
\r
9941 animInfo.piece = EmptySquare;
\r
9942 Explode(board, fromX, fromY, toX, toY);
\r
9945 /* Convert board position to corner of screen rect and color */
\r
9948 ScreenSquare(column, row, pt)
\r
9949 int column; int row; POINT * pt;
\r
9952 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9953 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9955 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9956 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9960 /* Generate a series of frame coords from start->mid->finish.
\r
9961 The movement rate doubles until the half way point is
\r
9962 reached, then halves back down to the final destination,
\r
9963 which gives a nice slow in/out effect. The algorithmn
\r
9964 may seem to generate too many intermediates for short
\r
9965 moves, but remember that the purpose is to attract the
\r
9966 viewers attention to the piece about to be moved and
\r
9967 then to where it ends up. Too few frames would be less
\r
9971 Tween(start, mid, finish, factor, frames, nFrames)
\r
9972 POINT * start; POINT * mid;
\r
9973 POINT * finish; int factor;
\r
9974 POINT frames[]; int * nFrames;
\r
9976 int n, fraction = 1, count = 0;
\r
9978 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9979 for (n = 0; n < factor; n++)
\r
9981 for (n = 0; n < factor; n++) {
\r
9982 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9983 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9985 fraction = fraction / 2;
\r
9989 frames[count] = *mid;
\r
9992 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9994 for (n = 0; n < factor; n++) {
\r
9995 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9996 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9998 fraction = fraction * 2;
\r
10000 *nFrames = count;
\r
10004 SettingsPopUp(ChessProgramState *cps)
\r
10005 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10006 EngineOptionsPopup(savedHwnd, cps);
\r
10009 int flock(int fid, int code)
\r
10011 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10013 ov.hEvent = NULL;
\r
10015 ov.OffsetHigh = 0;
\r
10017 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10018 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10019 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10020 default: return -1;
\r
10029 static char col[8][20];
\r
10030 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10032 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10037 ActivateTheme (int new)
\r
10038 { // Redo initialization of features depending on options that can occur in themes
\r
10040 if(new) InitDrawingColors();
\r
10041 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10042 InitDrawingSizes(boardSize, 0);
\r
10043 InvalidateRect(hwndMain, NULL, TRUE);
\r