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 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
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
234 int cliWidth, cliHeight;
\r
237 SizeInfo sizeInfo[] =
\r
239 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
240 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
241 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
242 { "petite", 33, 1, 1, 1, 0, 0 },
\r
243 { "slim", 37, 2, 1, 0, 0, 0 },
\r
244 { "small", 40, 2, 1, 0, 0, 0 },
\r
245 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
246 { "middling", 49, 2, 0, 0, 0, 0 },
\r
247 { "average", 54, 2, 0, 0, 0, 0 },
\r
248 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
249 { "medium", 64, 3, 0, 0, 0, 0 },
\r
250 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
251 { "large", 80, 3, 0, 0, 0, 0 },
\r
252 { "big", 87, 3, 0, 0, 0, 0 },
\r
253 { "huge", 95, 3, 0, 0, 0, 0 },
\r
254 { "giant", 108, 3, 0, 0, 0, 0 },
\r
255 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
256 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
257 { NULL, 0, 0, 0, 0, 0, 0 }
\r
260 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
261 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
263 { 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) },
\r
264 { 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) },
\r
265 { 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) },
\r
266 { 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) },
\r
267 { 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) },
\r
268 { 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) },
\r
269 { 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) },
\r
270 { 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) },
\r
271 { 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) },
\r
272 { 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) },
\r
273 { 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) },
\r
274 { 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) },
\r
275 { 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) },
\r
276 { 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) },
\r
277 { 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) },
\r
278 { 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) },
\r
279 { 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) },
\r
280 { 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) },
\r
283 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
292 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
293 #define N_BUTTONS 5
\r
295 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
297 {"<<", IDM_ToStart, NULL, NULL},
\r
298 {"<", IDM_Backward, NULL, NULL},
\r
299 {"P", IDM_Pause, NULL, NULL},
\r
300 {">", IDM_Forward, NULL, NULL},
\r
301 {">>", IDM_ToEnd, NULL, NULL},
\r
304 int tinyLayout = 0, smallLayout = 0;
\r
305 #define MENU_BAR_ITEMS 7
\r
306 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
307 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
308 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
312 MySound sounds[(int)NSoundClasses];
\r
313 MyTextAttribs textAttribs[(int)NColorClasses];
\r
315 MyColorizeAttribs colorizeAttribs[] = {
\r
316 { (COLORREF)0, 0, "Shout Text" },
\r
317 { (COLORREF)0, 0, "SShout/CShout" },
\r
318 { (COLORREF)0, 0, "Channel 1 Text" },
\r
319 { (COLORREF)0, 0, "Channel Text" },
\r
320 { (COLORREF)0, 0, "Kibitz Text" },
\r
321 { (COLORREF)0, 0, "Tell Text" },
\r
322 { (COLORREF)0, 0, "Challenge Text" },
\r
323 { (COLORREF)0, 0, "Request Text" },
\r
324 { (COLORREF)0, 0, "Seek Text" },
\r
325 { (COLORREF)0, 0, "Normal Text" },
\r
326 { (COLORREF)0, 0, "None" }
\r
331 static char *commentTitle;
\r
332 static char *commentText;
\r
333 static int commentIndex;
\r
334 static Boolean editComment = FALSE;
\r
337 char errorTitle[MSG_SIZ];
\r
338 char errorMessage[2*MSG_SIZ];
\r
339 HWND errorDialog = NULL;
\r
340 BOOLEAN moveErrorMessageUp = FALSE;
\r
341 BOOLEAN consoleEcho = TRUE;
\r
342 CHARFORMAT consoleCF;
\r
343 COLORREF consoleBackgroundColor;
\r
345 char *programVersion;
\r
351 typedef int CPKind;
\r
360 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
363 #define INPUT_SOURCE_BUF_SIZE 4096
\r
365 typedef struct _InputSource {
\r
372 char buf[INPUT_SOURCE_BUF_SIZE];
\r
376 InputCallback func;
\r
377 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
381 InputSource *consoleInputSource;
\r
386 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
387 VOID ConsoleCreate();
\r
389 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
390 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
391 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
392 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
394 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
395 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
396 void ParseIcsTextMenu(char *icsTextMenuString);
\r
397 VOID PopUpMoveDialog(char firstchar);
\r
398 VOID PopUpNameDialog(char firstchar);
\r
399 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
403 int GameListOptions();
\r
405 int dummy; // [HGM] for obsolete args
\r
407 HWND hwndMain = NULL; /* root window*/
\r
408 HWND hwndConsole = NULL;
\r
409 HWND commentDialog = NULL;
\r
410 HWND moveHistoryDialog = NULL;
\r
411 HWND evalGraphDialog = NULL;
\r
412 HWND engineOutputDialog = NULL;
\r
413 HWND gameListDialog = NULL;
\r
414 HWND editTagsDialog = NULL;
\r
416 int commentUp = FALSE;
\r
418 WindowPlacement wpMain;
\r
419 WindowPlacement wpConsole;
\r
420 WindowPlacement wpComment;
\r
421 WindowPlacement wpMoveHistory;
\r
422 WindowPlacement wpEvalGraph;
\r
423 WindowPlacement wpEngineOutput;
\r
424 WindowPlacement wpGameList;
\r
425 WindowPlacement wpTags;
\r
427 VOID EngineOptionsPopup(); // [HGM] settings
\r
429 VOID GothicPopUp(char *title, VariantClass variant);
\r
431 * Setting "frozen" should disable all user input other than deleting
\r
432 * the window. We do this while engines are initializing themselves.
\r
434 static int frozen = 0;
\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
441 if (frozen) return;
\r
443 hmenu = GetMenu(hwndMain);
\r
444 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
445 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
447 DrawMenuBar(hwndMain);
\r
450 /* Undo a FreezeUI */
\r
456 if (!frozen) return;
\r
458 hmenu = GetMenu(hwndMain);
\r
459 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
460 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
462 DrawMenuBar(hwndMain);
\r
465 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
467 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
473 #define JAWS_ALT_INTERCEPT
\r
474 #define JAWS_KB_NAVIGATION
\r
475 #define JAWS_MENU_ITEMS
\r
476 #define JAWS_SILENCE
\r
477 #define JAWS_REPLAY
\r
479 #define JAWS_COPYRIGHT
\r
480 #define JAWS_DELETE(X) X
\r
481 #define SAYMACHINEMOVE()
\r
485 /*---------------------------------------------------------------------------*\
\r
489 \*---------------------------------------------------------------------------*/
\r
492 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
493 LPSTR lpCmdLine, int nCmdShow)
\r
496 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
497 // INITCOMMONCONTROLSEX ex;
\r
501 LoadLibrary("RICHED32.DLL");
\r
502 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
504 if (!InitApplication(hInstance)) {
\r
507 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
513 // InitCommonControlsEx(&ex);
\r
514 InitCommonControls();
\r
516 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
517 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
518 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
520 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
522 while (GetMessage(&msg, /* message structure */
\r
523 NULL, /* handle of window receiving the message */
\r
524 0, /* lowest message to examine */
\r
525 0)) /* highest message to examine */
\r
528 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
529 // [HGM] navigate: switch between all windows with tab
\r
530 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
531 int i, currentElement = 0;
\r
533 // first determine what element of the chain we come from (if any)
\r
534 if(appData.icsActive) {
\r
535 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
536 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
538 if(engineOutputDialog && EngineOutputIsUp()) {
\r
539 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
540 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
542 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
543 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
545 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
546 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
547 if(msg.hwnd == e1) currentElement = 2; else
\r
548 if(msg.hwnd == e2) currentElement = 3; else
\r
549 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
550 if(msg.hwnd == mh) currentElement = 4; else
\r
551 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
552 if(msg.hwnd == hText) currentElement = 5; else
\r
553 if(msg.hwnd == hInput) currentElement = 6; else
\r
554 for (i = 0; i < N_BUTTONS; i++) {
\r
555 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
558 // determine where to go to
\r
559 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
561 currentElement = (currentElement + direction) % 7;
\r
562 switch(currentElement) {
\r
564 h = hwndMain; break; // passing this case always makes the loop exit
\r
566 h = buttonDesc[0].hwnd; break; // could be NULL
\r
568 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
571 if(!EngineOutputIsUp()) continue;
\r
574 if(!MoveHistoryIsUp()) continue;
\r
576 // case 6: // input to eval graph does not seem to get here!
\r
577 // if(!EvalGraphIsUp()) continue;
\r
578 // h = evalGraphDialog; break;
\r
580 if(!appData.icsActive) continue;
\r
584 if(!appData.icsActive) continue;
\r
590 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
591 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
594 continue; // this message now has been processed
\r
598 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
599 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
600 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
601 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
602 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
603 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
604 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
605 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
606 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
607 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
608 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
609 for(i=0; i<MAX_CHAT; i++)
\r
610 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
613 if(done) continue; // [HGM] chat: end patch
\r
614 TranslateMessage(&msg); /* Translates virtual key codes */
\r
615 DispatchMessage(&msg); /* Dispatches message to window */
\r
620 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
623 /*---------------------------------------------------------------------------*\
\r
625 * Initialization functions
\r
627 \*---------------------------------------------------------------------------*/
\r
631 { // update user logo if necessary
\r
632 static char oldUserName[MSG_SIZ], *curName;
\r
634 if(appData.autoLogo) {
\r
635 curName = UserName();
\r
636 if(strcmp(curName, oldUserName)) {
\r
637 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
638 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
639 strcpy(oldUserName, curName);
\r
645 InitApplication(HINSTANCE hInstance)
\r
649 /* Fill in window class structure with parameters that describe the */
\r
652 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
653 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
654 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
655 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
656 wc.hInstance = hInstance; /* Owner of this class */
\r
657 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
658 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
659 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
660 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
661 wc.lpszClassName = szAppName; /* Name to register as */
\r
663 /* Register the window class and return success/failure code. */
\r
664 if (!RegisterClass(&wc)) return FALSE;
\r
666 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
667 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
669 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
670 wc.hInstance = hInstance;
\r
671 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
672 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
673 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
674 wc.lpszMenuName = NULL;
\r
675 wc.lpszClassName = szConsoleName;
\r
677 if (!RegisterClass(&wc)) return FALSE;
\r
682 /* Set by InitInstance, used by EnsureOnScreen */
\r
683 int screenHeight, screenWidth;
\r
686 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
688 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
689 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
690 if (*x > screenWidth - 32) *x = 0;
\r
691 if (*y > screenHeight - 32) *y = 0;
\r
692 if (*x < minX) *x = minX;
\r
693 if (*y < minY) *y = minY;
\r
697 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
699 HWND hwnd; /* Main window handle. */
\r
701 WINDOWPLACEMENT wp;
\r
704 hInst = hInstance; /* Store instance handle in our global variable */
\r
705 programName = szAppName;
\r
707 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
708 *filepart = NULLCHAR;
\r
710 GetCurrentDirectory(MSG_SIZ, installDir);
\r
712 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
713 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
714 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
715 /* xboard, and older WinBoards, controlled the move sound with the
\r
716 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
717 always turn the option on (so that the backend will call us),
\r
718 then let the user turn the sound off by setting it to silence if
\r
719 desired. To accommodate old winboard.ini files saved by old
\r
720 versions of WinBoard, we also turn off the sound if the option
\r
721 was initially set to false. [HGM] taken out of InitAppData */
\r
722 if (!appData.ringBellAfterMoves) {
\r
723 sounds[(int)SoundMove].name = strdup("");
\r
724 appData.ringBellAfterMoves = TRUE;
\r
726 if (appData.debugMode) {
\r
727 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
728 setbuf(debugFP, NULL);
\r
733 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
734 // InitEngineUCI( installDir, &second );
\r
736 /* Create a main window for this application instance. */
\r
737 hwnd = CreateWindow(szAppName, szTitle,
\r
738 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
739 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
740 NULL, NULL, hInstance, NULL);
\r
743 /* If window could not be created, return "failure" */
\r
748 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
749 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
750 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
752 if (first.programLogo == NULL && appData.debugMode) {
\r
753 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
755 } else if(appData.autoLogo) {
\r
756 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
758 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
759 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
763 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
764 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
766 if (second.programLogo == NULL && appData.debugMode) {
\r
767 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
769 } else if(appData.autoLogo) {
\r
771 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
772 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
773 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
775 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
776 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
777 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
783 iconWhite = LoadIcon(hInstance, "icon_white");
\r
784 iconBlack = LoadIcon(hInstance, "icon_black");
\r
785 iconCurrent = iconWhite;
\r
786 InitDrawingColors();
\r
787 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
788 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
789 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
790 /* Compute window size for each board size, and use the largest
\r
791 size that fits on this screen as the default. */
\r
792 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
793 if (boardSize == (BoardSize)-1 &&
\r
794 winH <= screenHeight
\r
795 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
796 && winW <= screenWidth) {
\r
797 boardSize = (BoardSize)ibs;
\r
801 InitDrawingSizes(boardSize, 0);
\r
803 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
805 /* [AS] Load textures if specified */
\r
806 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
808 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
809 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
810 liteBackTextureMode = appData.liteBackTextureMode;
\r
812 if (liteBackTexture == NULL && appData.debugMode) {
\r
813 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
817 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
818 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
819 darkBackTextureMode = appData.darkBackTextureMode;
\r
821 if (darkBackTexture == NULL && appData.debugMode) {
\r
822 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
826 mysrandom( (unsigned) time(NULL) );
\r
828 /* [AS] Restore layout */
\r
829 if( wpMoveHistory.visible ) {
\r
830 MoveHistoryPopUp();
\r
833 if( wpEvalGraph.visible ) {
\r
837 if( wpEngineOutput.visible ) {
\r
838 EngineOutputPopUp();
\r
841 /* Make the window visible; update its client area; and return "success" */
\r
842 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
843 wp.length = sizeof(WINDOWPLACEMENT);
\r
845 wp.showCmd = nCmdShow;
\r
846 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
847 wp.rcNormalPosition.left = wpMain.x;
\r
848 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
849 wp.rcNormalPosition.top = wpMain.y;
\r
850 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
851 SetWindowPlacement(hwndMain, &wp);
\r
853 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
855 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
856 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
860 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
861 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
863 ShowWindow(hwndConsole, nCmdShow);
\r
864 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
865 char buf[MSG_SIZ], *p = buf, *q;
\r
866 strcpy(buf, appData.chatBoxes);
\r
868 q = strchr(p, ';');
\r
870 if(*p) ChatPopUp(p);
\r
873 SetActiveWindow(hwndConsole);
\r
875 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
876 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
885 HMENU hmenu = GetMenu(hwndMain);
\r
887 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
888 MF_BYCOMMAND|((appData.icsActive &&
\r
889 *appData.icsCommPort != NULLCHAR) ?
\r
890 MF_ENABLED : MF_GRAYED));
\r
891 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
892 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
893 MF_CHECKED : MF_UNCHECKED));
\r
896 //---------------------------------------------------------------------------------------------------------
\r
898 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
899 #define XBOARD FALSE
\r
901 #define OPTCHAR "/"
\r
902 #define SEPCHAR "="
\r
906 // front-end part of option handling
\r
909 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
911 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
912 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
915 lf->lfEscapement = 0;
\r
916 lf->lfOrientation = 0;
\r
917 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
918 lf->lfItalic = mfp->italic;
\r
919 lf->lfUnderline = mfp->underline;
\r
920 lf->lfStrikeOut = mfp->strikeout;
\r
921 lf->lfCharSet = mfp->charset;
\r
922 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
923 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
924 lf->lfQuality = DEFAULT_QUALITY;
\r
925 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
926 strcpy(lf->lfFaceName, mfp->faceName);
\r
930 CreateFontInMF(MyFont *mf)
\r
932 LFfromMFP(&mf->lf, &mf->mfp);
\r
933 if (mf->hf) DeleteObject(mf->hf);
\r
934 mf->hf = CreateFontIndirect(&mf->lf);
\r
937 // [HGM] This platform-dependent table provides the location for storing the color info
\r
939 colorVariable[] = {
\r
944 &highlightSquareColor,
\r
945 &premoveHighlightColor,
\r
947 &consoleBackgroundColor,
\r
948 &appData.fontForeColorWhite,
\r
949 &appData.fontBackColorWhite,
\r
950 &appData.fontForeColorBlack,
\r
951 &appData.fontBackColorBlack,
\r
952 &appData.evalHistColorWhite,
\r
953 &appData.evalHistColorBlack,
\r
954 &appData.highlightArrowColor,
\r
957 /* Command line font name parser. NULL name means do nothing.
\r
958 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
959 For backward compatibility, syntax without the colon is also
\r
960 accepted, but font names with digits in them won't work in that case.
\r
963 ParseFontName(char *name, MyFontParams *mfp)
\r
966 if (name == NULL) return;
\r
968 q = strchr(p, ':');
\r
970 if (q - p >= sizeof(mfp->faceName))
\r
971 ExitArgError("Font name too long:", name);
\r
972 memcpy(mfp->faceName, p, q - p);
\r
973 mfp->faceName[q - p] = NULLCHAR;
\r
977 while (*p && !isdigit(*p)) {
\r
979 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
980 ExitArgError("Font name too long:", name);
\r
982 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
985 if (!*p) ExitArgError("Font point size missing:", name);
\r
986 mfp->pointSize = (float) atof(p);
\r
987 mfp->bold = (strchr(p, 'b') != NULL);
\r
988 mfp->italic = (strchr(p, 'i') != NULL);
\r
989 mfp->underline = (strchr(p, 'u') != NULL);
\r
990 mfp->strikeout = (strchr(p, 's') != NULL);
\r
991 mfp->charset = DEFAULT_CHARSET;
\r
992 q = strchr(p, 'c');
\r
994 mfp->charset = (BYTE) atoi(q+1);
\r
998 ParseFont(char *name, int number)
\r
999 { // wrapper to shield back-end from 'font'
\r
1000 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1005 { // in WB we have a 2D array of fonts; this initializes their description
\r
1007 /* Point font array elements to structures and
\r
1008 parse default font names */
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 font[j][i] = &fontRec[j][i];
\r
1012 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1019 { // here we create the actual fonts from the selected descriptions
\r
1021 for (i=0; i<NUM_FONTS; i++) {
\r
1022 for (j=0; j<NUM_SIZES; j++) {
\r
1023 CreateFontInMF(font[j][i]);
\r
1027 /* Color name parser.
\r
1028 X version accepts X color names, but this one
\r
1029 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1031 ParseColorName(char *name)
\r
1033 int red, green, blue, count;
\r
1034 char buf[MSG_SIZ];
\r
1036 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1038 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1039 &red, &green, &blue);
\r
1042 sprintf(buf, "Can't parse color name %s", name);
\r
1043 DisplayError(buf, 0);
\r
1044 return RGB(0, 0, 0);
\r
1046 return PALETTERGB(red, green, blue);
\r
1050 ParseColor(int n, char *name)
\r
1051 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1052 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1056 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1058 char *e = argValue;
\r
1062 if (*e == 'b') eff |= CFE_BOLD;
\r
1063 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1064 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1065 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1066 else if (*e == '#' || isdigit(*e)) break;
\r
1070 *color = ParseColorName(e);
\r
1074 ParseTextAttribs(ColorClass cc, char *s)
\r
1075 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1076 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1077 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1081 ParseBoardSize(void *addr, char *name)
\r
1082 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1083 BoardSize bs = SizeTiny;
\r
1084 while (sizeInfo[bs].name != NULL) {
\r
1085 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1086 *(BoardSize *)addr = bs;
\r
1091 ExitArgError("Unrecognized board size value", name);
\r
1096 { // [HGM] import name from appData first
\r
1099 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1100 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1101 textAttribs[cc].sound.data = NULL;
\r
1102 MyLoadSound(&textAttribs[cc].sound);
\r
1104 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1105 textAttribs[cc].sound.name = strdup("");
\r
1106 textAttribs[cc].sound.data = NULL;
\r
1108 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1109 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1110 sounds[sc].data = NULL;
\r
1111 MyLoadSound(&sounds[sc]);
\r
1116 SetCommPortDefaults()
\r
1118 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1119 dcb.DCBlength = sizeof(DCB);
\r
1120 dcb.BaudRate = 9600;
\r
1121 dcb.fBinary = TRUE;
\r
1122 dcb.fParity = FALSE;
\r
1123 dcb.fOutxCtsFlow = FALSE;
\r
1124 dcb.fOutxDsrFlow = FALSE;
\r
1125 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1126 dcb.fDsrSensitivity = FALSE;
\r
1127 dcb.fTXContinueOnXoff = TRUE;
\r
1128 dcb.fOutX = FALSE;
\r
1130 dcb.fNull = FALSE;
\r
1131 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1132 dcb.fAbortOnError = FALSE;
\r
1134 dcb.Parity = SPACEPARITY;
\r
1135 dcb.StopBits = ONESTOPBIT;
\r
1138 // [HGM] args: these three cases taken out to stay in front-end
\r
1140 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1141 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1142 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1143 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1145 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1146 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1147 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1148 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1149 ad->argName, mfp->faceName, mfp->pointSize,
\r
1150 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1151 mfp->bold ? "b" : "",
\r
1152 mfp->italic ? "i" : "",
\r
1153 mfp->underline ? "u" : "",
\r
1154 mfp->strikeout ? "s" : "",
\r
1155 (int)mfp->charset);
\r
1161 { // [HGM] copy the names from the internal WB variables to appData
\r
1164 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1165 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1166 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1167 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1171 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1172 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1173 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1174 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1175 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1176 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1177 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1178 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1179 (ta->effects) ? " " : "",
\r
1180 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1184 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1185 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1186 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1187 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1188 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1192 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1193 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1194 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1198 ParseCommPortSettings(char *s)
\r
1199 { // wrapper to keep dcb from back-end
\r
1200 ParseCommSettings(s, &dcb);
\r
1205 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1206 GetActualPlacement(hwndMain, &wpMain);
\r
1207 GetActualPlacement(hwndConsole, &wpConsole);
\r
1208 GetActualPlacement(commentDialog, &wpComment);
\r
1209 GetActualPlacement(editTagsDialog, &wpTags);
\r
1210 GetActualPlacement(gameListDialog, &wpGameList);
\r
1211 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1212 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1213 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1217 PrintCommPortSettings(FILE *f, char *name)
\r
1218 { // wrapper to shield back-end from DCB
\r
1219 PrintCommSettings(f, name, &dcb);
\r
1223 MySearchPath(char *installDir, char *name, char *fullname)
\r
1226 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1230 MyGetFullPathName(char *name, char *fullname)
\r
1233 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1238 { // [HGM] args: allows testing if main window is realized from back-end
\r
1239 return hwndMain != NULL;
\r
1243 PopUpStartupDialog()
\r
1247 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1248 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1249 FreeProcInstance(lpProc);
\r
1252 /*---------------------------------------------------------------------------*\
\r
1254 * GDI board drawing routines
\r
1256 \*---------------------------------------------------------------------------*/
\r
1258 /* [AS] Draw square using background texture */
\r
1259 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1264 return; /* Should never happen! */
\r
1267 SetGraphicsMode( dst, GM_ADVANCED );
\r
1274 /* X reflection */
\r
1279 x.eDx = (FLOAT) dw + dx - 1;
\r
1282 SetWorldTransform( dst, &x );
\r
1285 /* Y reflection */
\r
1291 x.eDy = (FLOAT) dh + dy - 1;
\r
1293 SetWorldTransform( dst, &x );
\r
1301 x.eDx = (FLOAT) dx;
\r
1302 x.eDy = (FLOAT) dy;
\r
1305 SetWorldTransform( dst, &x );
\r
1309 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1317 SetWorldTransform( dst, &x );
\r
1319 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1322 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1324 PM_WP = (int) WhitePawn,
\r
1325 PM_WN = (int) WhiteKnight,
\r
1326 PM_WB = (int) WhiteBishop,
\r
1327 PM_WR = (int) WhiteRook,
\r
1328 PM_WQ = (int) WhiteQueen,
\r
1329 PM_WF = (int) WhiteFerz,
\r
1330 PM_WW = (int) WhiteWazir,
\r
1331 PM_WE = (int) WhiteAlfil,
\r
1332 PM_WM = (int) WhiteMan,
\r
1333 PM_WO = (int) WhiteCannon,
\r
1334 PM_WU = (int) WhiteUnicorn,
\r
1335 PM_WH = (int) WhiteNightrider,
\r
1336 PM_WA = (int) WhiteAngel,
\r
1337 PM_WC = (int) WhiteMarshall,
\r
1338 PM_WAB = (int) WhiteCardinal,
\r
1339 PM_WD = (int) WhiteDragon,
\r
1340 PM_WL = (int) WhiteLance,
\r
1341 PM_WS = (int) WhiteCobra,
\r
1342 PM_WV = (int) WhiteFalcon,
\r
1343 PM_WSG = (int) WhiteSilver,
\r
1344 PM_WG = (int) WhiteGrasshopper,
\r
1345 PM_WK = (int) WhiteKing,
\r
1346 PM_BP = (int) BlackPawn,
\r
1347 PM_BN = (int) BlackKnight,
\r
1348 PM_BB = (int) BlackBishop,
\r
1349 PM_BR = (int) BlackRook,
\r
1350 PM_BQ = (int) BlackQueen,
\r
1351 PM_BF = (int) BlackFerz,
\r
1352 PM_BW = (int) BlackWazir,
\r
1353 PM_BE = (int) BlackAlfil,
\r
1354 PM_BM = (int) BlackMan,
\r
1355 PM_BO = (int) BlackCannon,
\r
1356 PM_BU = (int) BlackUnicorn,
\r
1357 PM_BH = (int) BlackNightrider,
\r
1358 PM_BA = (int) BlackAngel,
\r
1359 PM_BC = (int) BlackMarshall,
\r
1360 PM_BG = (int) BlackGrasshopper,
\r
1361 PM_BAB = (int) BlackCardinal,
\r
1362 PM_BD = (int) BlackDragon,
\r
1363 PM_BL = (int) BlackLance,
\r
1364 PM_BS = (int) BlackCobra,
\r
1365 PM_BV = (int) BlackFalcon,
\r
1366 PM_BSG = (int) BlackSilver,
\r
1367 PM_BK = (int) BlackKing
\r
1370 static HFONT hPieceFont = NULL;
\r
1371 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1372 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1373 static int fontBitmapSquareSize = 0;
\r
1374 static char pieceToFontChar[(int) EmptySquare] =
\r
1375 { 'p', 'n', 'b', 'r', 'q',
\r
1376 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1377 'k', 'o', 'm', 'v', 't', 'w',
\r
1378 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1381 extern BOOL SetCharTable( char *table, const char * map );
\r
1382 /* [HGM] moved to backend.c */
\r
1384 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1387 BYTE r1 = GetRValue( color );
\r
1388 BYTE g1 = GetGValue( color );
\r
1389 BYTE b1 = GetBValue( color );
\r
1395 /* Create a uniform background first */
\r
1396 hbrush = CreateSolidBrush( color );
\r
1397 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1398 FillRect( hdc, &rc, hbrush );
\r
1399 DeleteObject( hbrush );
\r
1402 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1403 int steps = squareSize / 2;
\r
1406 for( i=0; i<steps; i++ ) {
\r
1407 BYTE r = r1 - (r1-r2) * i / steps;
\r
1408 BYTE g = g1 - (g1-g2) * i / steps;
\r
1409 BYTE b = b1 - (b1-b2) * i / steps;
\r
1411 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1412 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1413 FillRect( hdc, &rc, hbrush );
\r
1414 DeleteObject(hbrush);
\r
1417 else if( mode == 2 ) {
\r
1418 /* Diagonal gradient, good more or less for every piece */
\r
1419 POINT triangle[3];
\r
1420 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1421 HBRUSH hbrush_old;
\r
1422 int steps = squareSize;
\r
1425 triangle[0].x = squareSize - steps;
\r
1426 triangle[0].y = squareSize;
\r
1427 triangle[1].x = squareSize;
\r
1428 triangle[1].y = squareSize;
\r
1429 triangle[2].x = squareSize;
\r
1430 triangle[2].y = squareSize - steps;
\r
1432 for( i=0; i<steps; i++ ) {
\r
1433 BYTE r = r1 - (r1-r2) * i / steps;
\r
1434 BYTE g = g1 - (g1-g2) * i / steps;
\r
1435 BYTE b = b1 - (b1-b2) * i / steps;
\r
1437 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1438 hbrush_old = SelectObject( hdc, hbrush );
\r
1439 Polygon( hdc, triangle, 3 );
\r
1440 SelectObject( hdc, hbrush_old );
\r
1441 DeleteObject(hbrush);
\r
1446 SelectObject( hdc, hpen );
\r
1451 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1452 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1453 piece: follow the steps as explained below.
\r
1455 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1459 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1463 int backColor = whitePieceColor;
\r
1464 int foreColor = blackPieceColor;
\r
1466 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1467 backColor = appData.fontBackColorWhite;
\r
1468 foreColor = appData.fontForeColorWhite;
\r
1470 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1471 backColor = appData.fontBackColorBlack;
\r
1472 foreColor = appData.fontForeColorBlack;
\r
1476 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1478 hbm_old = SelectObject( hdc, hbm );
\r
1482 rc.right = squareSize;
\r
1483 rc.bottom = squareSize;
\r
1485 /* Step 1: background is now black */
\r
1486 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1488 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1490 pt.x = (squareSize - sz.cx) / 2;
\r
1491 pt.y = (squareSize - sz.cy) / 2;
\r
1493 SetBkMode( hdc, TRANSPARENT );
\r
1494 SetTextColor( hdc, chroma );
\r
1495 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1496 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1498 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1499 /* Step 3: the area outside the piece is filled with white */
\r
1500 // FloodFill( hdc, 0, 0, chroma );
\r
1501 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1502 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1503 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1504 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1505 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1507 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1508 but if the start point is not inside the piece we're lost!
\r
1509 There should be a better way to do this... if we could create a region or path
\r
1510 from the fill operation we would be fine for example.
\r
1512 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1513 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1515 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1516 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1517 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1519 SelectObject( dc2, bm2 );
\r
1520 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1521 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1522 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1523 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1524 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1527 DeleteObject( bm2 );
\r
1530 SetTextColor( hdc, 0 );
\r
1532 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1533 draw the piece again in black for safety.
\r
1535 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1537 SelectObject( hdc, hbm_old );
\r
1539 if( hPieceMask[index] != NULL ) {
\r
1540 DeleteObject( hPieceMask[index] );
\r
1543 hPieceMask[index] = hbm;
\r
1546 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1548 SelectObject( hdc, hbm );
\r
1551 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1552 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1553 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1555 SelectObject( dc1, hPieceMask[index] );
\r
1556 SelectObject( dc2, bm2 );
\r
1557 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1558 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1561 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1562 the piece background and deletes (makes transparent) the rest.
\r
1563 Thanks to that mask, we are free to paint the background with the greates
\r
1564 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1565 We use this, to make gradients and give the pieces a "roundish" look.
\r
1567 SetPieceBackground( hdc, backColor, 2 );
\r
1568 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1572 DeleteObject( bm2 );
\r
1575 SetTextColor( hdc, foreColor );
\r
1576 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1578 SelectObject( hdc, hbm_old );
\r
1580 if( hPieceFace[index] != NULL ) {
\r
1581 DeleteObject( hPieceFace[index] );
\r
1584 hPieceFace[index] = hbm;
\r
1587 static int TranslatePieceToFontPiece( int piece )
\r
1617 case BlackMarshall:
\r
1621 case BlackNightrider:
\r
1627 case BlackUnicorn:
\r
1631 case BlackGrasshopper:
\r
1643 case BlackCardinal:
\r
1650 case WhiteMarshall:
\r
1654 case WhiteNightrider:
\r
1660 case WhiteUnicorn:
\r
1664 case WhiteGrasshopper:
\r
1676 case WhiteCardinal:
\r
1685 void CreatePiecesFromFont()
\r
1688 HDC hdc_window = NULL;
\r
1694 if( fontBitmapSquareSize < 0 ) {
\r
1695 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1699 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1700 fontBitmapSquareSize = -1;
\r
1704 if( fontBitmapSquareSize != squareSize ) {
\r
1705 hdc_window = GetDC( hwndMain );
\r
1706 hdc = CreateCompatibleDC( hdc_window );
\r
1708 if( hPieceFont != NULL ) {
\r
1709 DeleteObject( hPieceFont );
\r
1712 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1713 hPieceMask[i] = NULL;
\r
1714 hPieceFace[i] = NULL;
\r
1720 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1721 fontHeight = appData.fontPieceSize;
\r
1724 fontHeight = (fontHeight * squareSize) / 100;
\r
1726 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1728 lf.lfEscapement = 0;
\r
1729 lf.lfOrientation = 0;
\r
1730 lf.lfWeight = FW_NORMAL;
\r
1732 lf.lfUnderline = 0;
\r
1733 lf.lfStrikeOut = 0;
\r
1734 lf.lfCharSet = DEFAULT_CHARSET;
\r
1735 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1736 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1737 lf.lfQuality = PROOF_QUALITY;
\r
1738 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1739 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1740 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1742 hPieceFont = CreateFontIndirect( &lf );
\r
1744 if( hPieceFont == NULL ) {
\r
1745 fontBitmapSquareSize = -2;
\r
1748 /* Setup font-to-piece character table */
\r
1749 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1750 /* No (or wrong) global settings, try to detect the font */
\r
1751 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1753 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1755 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1756 /* DiagramTT* family */
\r
1757 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1759 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1760 /* Fairy symbols */
\r
1761 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1763 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1764 /* Good Companion (Some characters get warped as literal :-( */
\r
1765 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1766 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1767 SetCharTable(pieceToFontChar, s);
\r
1770 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1771 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1775 /* Create bitmaps */
\r
1776 hfont_old = SelectObject( hdc, hPieceFont );
\r
1777 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1778 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1779 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1781 SelectObject( hdc, hfont_old );
\r
1783 fontBitmapSquareSize = squareSize;
\r
1787 if( hdc != NULL ) {
\r
1791 if( hdc_window != NULL ) {
\r
1792 ReleaseDC( hwndMain, hdc_window );
\r
1797 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1801 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1802 if (gameInfo.event &&
\r
1803 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1804 strcmp(name, "k80s") == 0) {
\r
1805 strcpy(name, "tim");
\r
1807 return LoadBitmap(hinst, name);
\r
1811 /* Insert a color into the program's logical palette
\r
1812 structure. This code assumes the given color is
\r
1813 the result of the RGB or PALETTERGB macro, and it
\r
1814 knows how those macros work (which is documented).
\r
1817 InsertInPalette(COLORREF color)
\r
1819 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1821 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1822 DisplayFatalError("Too many colors", 0, 1);
\r
1823 pLogPal->palNumEntries--;
\r
1827 pe->peFlags = (char) 0;
\r
1828 pe->peRed = (char) (0xFF & color);
\r
1829 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1830 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1836 InitDrawingColors()
\r
1838 if (pLogPal == NULL) {
\r
1839 /* Allocate enough memory for a logical palette with
\r
1840 * PALETTESIZE entries and set the size and version fields
\r
1841 * of the logical palette structure.
\r
1843 pLogPal = (NPLOGPALETTE)
\r
1844 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1845 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1846 pLogPal->palVersion = 0x300;
\r
1848 pLogPal->palNumEntries = 0;
\r
1850 InsertInPalette(lightSquareColor);
\r
1851 InsertInPalette(darkSquareColor);
\r
1852 InsertInPalette(whitePieceColor);
\r
1853 InsertInPalette(blackPieceColor);
\r
1854 InsertInPalette(highlightSquareColor);
\r
1855 InsertInPalette(premoveHighlightColor);
\r
1857 /* create a logical color palette according the information
\r
1858 * in the LOGPALETTE structure.
\r
1860 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1862 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1863 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1864 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1865 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1866 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1867 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1868 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1869 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1870 /* [AS] Force rendering of the font-based pieces */
\r
1871 if( fontBitmapSquareSize > 0 ) {
\r
1872 fontBitmapSquareSize = 0;
\r
1878 BoardWidth(int boardSize, int n)
\r
1879 { /* [HGM] argument n added to allow different width and height */
\r
1880 int lineGap = sizeInfo[boardSize].lineGap;
\r
1882 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1883 lineGap = appData.overrideLineGap;
\r
1886 return (n + 1) * lineGap +
\r
1887 n * sizeInfo[boardSize].squareSize;
\r
1890 /* Respond to board resize by dragging edge */
\r
1892 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1894 BoardSize newSize = NUM_SIZES - 1;
\r
1895 static int recurse = 0;
\r
1896 if (IsIconic(hwndMain)) return;
\r
1897 if (recurse > 0) return;
\r
1899 while (newSize > 0) {
\r
1900 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1901 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1902 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1905 boardSize = newSize;
\r
1906 InitDrawingSizes(boardSize, flags);
\r
1911 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1914 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1916 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1917 ChessSquare piece;
\r
1918 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1920 SIZE clockSize, messageSize;
\r
1922 char buf[MSG_SIZ];
\r
1924 HMENU hmenu = GetMenu(hwndMain);
\r
1925 RECT crect, wrect, oldRect;
\r
1927 LOGBRUSH logbrush;
\r
1929 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1930 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1932 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1933 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1935 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1936 oldRect.top = wpMain.y;
\r
1937 oldRect.right = wpMain.x + wpMain.width;
\r
1938 oldRect.bottom = wpMain.y + wpMain.height;
\r
1940 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1941 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1942 squareSize = sizeInfo[boardSize].squareSize;
\r
1943 lineGap = sizeInfo[boardSize].lineGap;
\r
1944 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1946 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1947 lineGap = appData.overrideLineGap;
\r
1950 if (tinyLayout != oldTinyLayout) {
\r
1951 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1953 style &= ~WS_SYSMENU;
\r
1954 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1955 "&Minimize\tCtrl+F4");
\r
1957 style |= WS_SYSMENU;
\r
1958 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1960 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1962 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1963 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1964 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1966 DrawMenuBar(hwndMain);
\r
1969 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1970 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1972 /* Get text area sizes */
\r
1973 hdc = GetDC(hwndMain);
\r
1974 if (appData.clockMode) {
\r
1975 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1977 sprintf(buf, "White");
\r
1979 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1980 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1981 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1982 str = "We only care about the height here";
\r
1983 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1984 SelectObject(hdc, oldFont);
\r
1985 ReleaseDC(hwndMain, hdc);
\r
1987 /* Compute where everything goes */
\r
1988 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1989 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1990 logoHeight = 2*clockSize.cy;
\r
1991 leftLogoRect.left = OUTER_MARGIN;
\r
1992 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1993 leftLogoRect.top = OUTER_MARGIN;
\r
1994 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1996 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1997 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1998 rightLogoRect.top = OUTER_MARGIN;
\r
1999 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2002 whiteRect.left = leftLogoRect.right;
\r
2003 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2004 whiteRect.top = OUTER_MARGIN;
\r
2005 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2007 blackRect.right = rightLogoRect.left;
\r
2008 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2009 blackRect.top = whiteRect.top;
\r
2010 blackRect.bottom = whiteRect.bottom;
\r
2012 whiteRect.left = OUTER_MARGIN;
\r
2013 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2014 whiteRect.top = OUTER_MARGIN;
\r
2015 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2017 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2018 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2019 blackRect.top = whiteRect.top;
\r
2020 blackRect.bottom = whiteRect.bottom;
\r
2022 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2025 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2026 if (appData.showButtonBar) {
\r
2027 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2028 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2030 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2032 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2033 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2035 boardRect.left = OUTER_MARGIN;
\r
2036 boardRect.right = boardRect.left + boardWidth;
\r
2037 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2038 boardRect.bottom = boardRect.top + boardHeight;
\r
2040 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2041 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2042 oldBoardSize = boardSize;
\r
2043 oldTinyLayout = tinyLayout;
\r
2044 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2045 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2046 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2047 winW *= 1 + twoBoards;
\r
2048 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2049 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2050 wpMain.height = winH; // without disturbing window attachments
\r
2051 GetWindowRect(hwndMain, &wrect);
\r
2052 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2053 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2055 // [HGM] placement: let attached windows follow size change.
\r
2056 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2057 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2058 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2059 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2060 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2062 /* compensate if menu bar wrapped */
\r
2063 GetClientRect(hwndMain, &crect);
\r
2064 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2065 wpMain.height += offby;
\r
2067 case WMSZ_TOPLEFT:
\r
2068 SetWindowPos(hwndMain, NULL,
\r
2069 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2070 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2073 case WMSZ_TOPRIGHT:
\r
2075 SetWindowPos(hwndMain, NULL,
\r
2076 wrect.left, wrect.bottom - wpMain.height,
\r
2077 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2080 case WMSZ_BOTTOMLEFT:
\r
2082 SetWindowPos(hwndMain, NULL,
\r
2083 wrect.right - wpMain.width, wrect.top,
\r
2084 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2087 case WMSZ_BOTTOMRIGHT:
\r
2091 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2092 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2097 for (i = 0; i < N_BUTTONS; i++) {
\r
2098 if (buttonDesc[i].hwnd != NULL) {
\r
2099 DestroyWindow(buttonDesc[i].hwnd);
\r
2100 buttonDesc[i].hwnd = NULL;
\r
2102 if (appData.showButtonBar) {
\r
2103 buttonDesc[i].hwnd =
\r
2104 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2105 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2106 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2107 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2108 (HMENU) buttonDesc[i].id,
\r
2109 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2111 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2112 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2113 MAKELPARAM(FALSE, 0));
\r
2115 if (buttonDesc[i].id == IDM_Pause)
\r
2116 hwndPause = buttonDesc[i].hwnd;
\r
2117 buttonDesc[i].wndproc = (WNDPROC)
\r
2118 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2121 if (gridPen != NULL) DeleteObject(gridPen);
\r
2122 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2123 if (premovePen != NULL) DeleteObject(premovePen);
\r
2124 if (lineGap != 0) {
\r
2125 logbrush.lbStyle = BS_SOLID;
\r
2126 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2128 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2129 lineGap, &logbrush, 0, NULL);
\r
2130 logbrush.lbColor = highlightSquareColor;
\r
2132 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2133 lineGap, &logbrush, 0, NULL);
\r
2135 logbrush.lbColor = premoveHighlightColor;
\r
2137 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2138 lineGap, &logbrush, 0, NULL);
\r
2140 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2141 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2142 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2143 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2144 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2145 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2146 BOARD_WIDTH * (squareSize + lineGap);
\r
2147 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2149 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2150 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2151 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2152 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2153 lineGap / 2 + (i * (squareSize + lineGap));
\r
2154 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2155 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2156 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2160 /* [HGM] Licensing requirement */
\r
2162 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2165 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2167 GothicPopUp( "", VariantNormal);
\r
2170 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2172 /* Load piece bitmaps for this board size */
\r
2173 for (i=0; i<=2; i++) {
\r
2174 for (piece = WhitePawn;
\r
2175 (int) piece < (int) BlackPawn;
\r
2176 piece = (ChessSquare) ((int) piece + 1)) {
\r
2177 if (pieceBitmap[i][piece] != NULL)
\r
2178 DeleteObject(pieceBitmap[i][piece]);
\r
2182 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2183 // Orthodox Chess pieces
\r
2184 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2185 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2186 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2187 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2188 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2189 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2190 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2191 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2192 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2193 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2194 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2195 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2196 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2197 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2198 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2199 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2200 // in Shogi, Hijack the unused Queen for Lance
\r
2201 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2202 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2203 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2205 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2206 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2207 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2210 if(squareSize <= 72 && squareSize >= 33) {
\r
2211 /* A & C are available in most sizes now */
\r
2212 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2213 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2214 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2215 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2216 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2217 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2218 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2219 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2220 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2221 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2222 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2223 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2224 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2225 } else { // Smirf-like
\r
2226 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2227 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2228 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2230 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2231 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2232 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2233 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2234 } else { // WinBoard standard
\r
2235 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2236 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2237 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2242 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2243 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2244 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2245 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2246 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2247 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2248 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2249 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2250 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2251 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2252 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2253 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2254 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2255 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2256 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2257 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2258 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2259 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2260 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2261 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2262 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2263 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2264 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2265 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2266 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2267 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2268 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2269 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2270 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2271 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2272 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2274 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2275 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2276 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2277 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2278 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2279 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2280 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2281 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2282 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2283 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2284 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2285 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2286 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2288 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2289 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2290 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2291 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2292 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2293 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2294 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2295 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2296 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2297 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2298 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2299 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2302 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2303 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2304 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2305 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2306 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2307 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2308 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2309 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2310 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2311 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2312 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2313 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2314 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2315 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2316 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2320 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2321 /* special Shogi support in this size */
\r
2322 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2323 for (piece = WhitePawn;
\r
2324 (int) piece < (int) BlackPawn;
\r
2325 piece = (ChessSquare) ((int) piece + 1)) {
\r
2326 if (pieceBitmap[i][piece] != NULL)
\r
2327 DeleteObject(pieceBitmap[i][piece]);
\r
2330 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2331 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2332 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2333 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2334 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2335 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2336 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2337 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2342 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2344 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2345 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2346 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2347 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2348 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2349 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2350 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2351 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2356 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2358 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2359 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2360 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2361 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2362 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2363 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2364 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2365 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2370 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2377 PieceBitmap(ChessSquare p, int kind)
\r
2379 if ((int) p >= (int) BlackPawn)
\r
2380 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2382 return pieceBitmap[kind][(int) p];
\r
2385 /***************************************************************/
\r
2387 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2388 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2390 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2391 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2395 SquareToPos(int row, int column, int * x, int * y)
\r
2398 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2399 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2401 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2402 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2407 DrawCoordsOnDC(HDC hdc)
\r
2409 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2410 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2411 char str[2] = { NULLCHAR, NULLCHAR };
\r
2412 int oldMode, oldAlign, x, y, start, i;
\r
2416 if (!appData.showCoords)
\r
2419 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2421 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2422 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2423 oldAlign = GetTextAlign(hdc);
\r
2424 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2426 y = boardRect.top + lineGap;
\r
2427 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2429 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2430 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2431 str[0] = files[start + i];
\r
2432 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2433 y += squareSize + lineGap;
\r
2436 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2438 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2439 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2440 str[0] = ranks[start + i];
\r
2441 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2442 x += squareSize + lineGap;
\r
2445 SelectObject(hdc, oldBrush);
\r
2446 SetBkMode(hdc, oldMode);
\r
2447 SetTextAlign(hdc, oldAlign);
\r
2448 SelectObject(hdc, oldFont);
\r
2452 DrawGridOnDC(HDC hdc)
\r
2456 if (lineGap != 0) {
\r
2457 oldPen = SelectObject(hdc, gridPen);
\r
2458 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2459 SelectObject(hdc, oldPen);
\r
2463 #define HIGHLIGHT_PEN 0
\r
2464 #define PREMOVE_PEN 1
\r
2467 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2470 HPEN oldPen, hPen;
\r
2471 if (lineGap == 0) return;
\r
2473 x1 = boardRect.left +
\r
2474 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2475 y1 = boardRect.top +
\r
2476 lineGap/2 + y * (squareSize + lineGap);
\r
2478 x1 = boardRect.left +
\r
2479 lineGap/2 + x * (squareSize + lineGap);
\r
2480 y1 = boardRect.top +
\r
2481 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2483 hPen = pen ? premovePen : highlightPen;
\r
2484 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2485 MoveToEx(hdc, x1, y1, NULL);
\r
2486 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2487 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2488 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2489 LineTo(hdc, x1, y1);
\r
2490 SelectObject(hdc, oldPen);
\r
2494 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2497 for (i=0; i<2; i++) {
\r
2498 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2499 DrawHighlightOnDC(hdc, TRUE,
\r
2500 h->sq[i].x, h->sq[i].y,
\r
2505 /* Note: sqcolor is used only in monoMode */
\r
2506 /* Note that this code is largely duplicated in woptions.c,
\r
2507 function DrawSampleSquare, so that needs to be updated too */
\r
2509 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2511 HBITMAP oldBitmap;
\r
2515 if (appData.blindfold) return;
\r
2517 /* [AS] Use font-based pieces if needed */
\r
2518 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2519 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2520 CreatePiecesFromFont();
\r
2522 if( fontBitmapSquareSize == squareSize ) {
\r
2523 int index = TranslatePieceToFontPiece(piece);
\r
2525 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2529 squareSize, squareSize,
\r
2534 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2538 squareSize, squareSize,
\r
2547 if (appData.monoMode) {
\r
2548 SelectObject(tmphdc, PieceBitmap(piece,
\r
2549 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2550 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2551 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2553 tmpSize = squareSize;
\r
2555 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2556 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2557 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2558 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2559 x += (squareSize - minorSize)>>1;
\r
2560 y += squareSize - minorSize - 2;
\r
2561 tmpSize = minorSize;
\r
2563 if (color || appData.allWhite ) {
\r
2564 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2566 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2567 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2568 if(appData.upsideDown && color==flipView)
\r
2569 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2571 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2572 /* Use black for outline of white pieces */
\r
2573 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2574 if(appData.upsideDown && color==flipView)
\r
2575 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2577 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2579 /* Use square color for details of black pieces */
\r
2580 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2581 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2582 if(appData.upsideDown && !flipView)
\r
2583 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2585 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2587 SelectObject(hdc, oldBrush);
\r
2588 SelectObject(tmphdc, oldBitmap);
\r
2592 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2593 int GetBackTextureMode( int algo )
\r
2595 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2599 case BACK_TEXTURE_MODE_PLAIN:
\r
2600 result = 1; /* Always use identity map */
\r
2602 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2603 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2611 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2612 to handle redraws cleanly (as random numbers would always be different).
\r
2614 VOID RebuildTextureSquareInfo()
\r
2624 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2626 if( liteBackTexture != NULL ) {
\r
2627 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2628 lite_w = bi.bmWidth;
\r
2629 lite_h = bi.bmHeight;
\r
2633 if( darkBackTexture != NULL ) {
\r
2634 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2635 dark_w = bi.bmWidth;
\r
2636 dark_h = bi.bmHeight;
\r
2640 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2641 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2642 if( (col + row) & 1 ) {
\r
2644 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2645 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2646 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2647 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2652 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2653 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2654 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2655 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2662 /* [AS] Arrow highlighting support */
\r
2664 static int A_WIDTH = 5; /* Width of arrow body */
\r
2666 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2667 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2669 static double Sqr( double x )
\r
2674 static int Round( double x )
\r
2676 return (int) (x + 0.5);
\r
2679 /* Draw an arrow between two points using current settings */
\r
2680 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2683 double dx, dy, j, k, x, y;
\r
2685 if( d_x == s_x ) {
\r
2686 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2688 arrow[0].x = s_x + A_WIDTH;
\r
2691 arrow[1].x = s_x + A_WIDTH;
\r
2692 arrow[1].y = d_y - h;
\r
2694 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2695 arrow[2].y = d_y - h;
\r
2700 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2701 arrow[4].y = d_y - h;
\r
2703 arrow[5].x = s_x - A_WIDTH;
\r
2704 arrow[5].y = d_y - h;
\r
2706 arrow[6].x = s_x - A_WIDTH;
\r
2709 else if( d_y == s_y ) {
\r
2710 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2713 arrow[0].y = s_y + A_WIDTH;
\r
2715 arrow[1].x = d_x - w;
\r
2716 arrow[1].y = s_y + A_WIDTH;
\r
2718 arrow[2].x = d_x - w;
\r
2719 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2724 arrow[4].x = d_x - w;
\r
2725 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2727 arrow[5].x = d_x - w;
\r
2728 arrow[5].y = s_y - A_WIDTH;
\r
2731 arrow[6].y = s_y - A_WIDTH;
\r
2734 /* [AS] Needed a lot of paper for this! :-) */
\r
2735 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2736 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2738 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2740 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2745 arrow[0].x = Round(x - j);
\r
2746 arrow[0].y = Round(y + j*dx);
\r
2748 arrow[1].x = Round(x + j);
\r
2749 arrow[1].y = Round(y - j*dx);
\r
2752 x = (double) d_x - k;
\r
2753 y = (double) d_y - k*dy;
\r
2756 x = (double) d_x + k;
\r
2757 y = (double) d_y + k*dy;
\r
2760 arrow[2].x = Round(x + j);
\r
2761 arrow[2].y = Round(y - j*dx);
\r
2763 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2764 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2769 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2770 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2772 arrow[6].x = Round(x - j);
\r
2773 arrow[6].y = Round(y + j*dx);
\r
2776 Polygon( hdc, arrow, 7 );
\r
2779 /* [AS] Draw an arrow between two squares */
\r
2780 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2782 int s_x, s_y, d_x, d_y;
\r
2789 if( s_col == d_col && s_row == d_row ) {
\r
2793 /* Get source and destination points */
\r
2794 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2795 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2798 d_y += squareSize / 4;
\r
2800 else if( d_y < s_y ) {
\r
2801 d_y += 3 * squareSize / 4;
\r
2804 d_y += squareSize / 2;
\r
2808 d_x += squareSize / 4;
\r
2810 else if( d_x < s_x ) {
\r
2811 d_x += 3 * squareSize / 4;
\r
2814 d_x += squareSize / 2;
\r
2817 s_x += squareSize / 2;
\r
2818 s_y += squareSize / 2;
\r
2820 /* Adjust width */
\r
2821 A_WIDTH = squareSize / 14;
\r
2824 stLB.lbStyle = BS_SOLID;
\r
2825 stLB.lbColor = appData.highlightArrowColor;
\r
2828 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2829 holdpen = SelectObject( hdc, hpen );
\r
2830 hbrush = CreateBrushIndirect( &stLB );
\r
2831 holdbrush = SelectObject( hdc, hbrush );
\r
2833 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2835 SelectObject( hdc, holdpen );
\r
2836 SelectObject( hdc, holdbrush );
\r
2837 DeleteObject( hpen );
\r
2838 DeleteObject( hbrush );
\r
2841 BOOL HasHighlightInfo()
\r
2843 BOOL result = FALSE;
\r
2845 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2846 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2854 BOOL IsDrawArrowEnabled()
\r
2856 BOOL result = FALSE;
\r
2858 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2865 VOID DrawArrowHighlight( HDC hdc )
\r
2867 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2868 DrawArrowBetweenSquares( hdc,
\r
2869 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2870 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2874 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2876 HRGN result = NULL;
\r
2878 if( HasHighlightInfo() ) {
\r
2879 int x1, y1, x2, y2;
\r
2880 int sx, sy, dx, dy;
\r
2882 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2883 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2885 sx = MIN( x1, x2 );
\r
2886 sy = MIN( y1, y2 );
\r
2887 dx = MAX( x1, x2 ) + squareSize;
\r
2888 dy = MAX( y1, y2 ) + squareSize;
\r
2890 result = CreateRectRgn( sx, sy, dx, dy );
\r
2897 Warning: this function modifies the behavior of several other functions.
\r
2899 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2900 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2901 repaint is scattered all over the place, which is not good for features such as
\r
2902 "arrow highlighting" that require a full repaint of the board.
\r
2904 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2905 user interaction, when speed is not so important) but especially to avoid errors
\r
2906 in the displayed graphics.
\r
2908 In such patched places, I always try refer to this function so there is a single
\r
2909 place to maintain knowledge.
\r
2911 To restore the original behavior, just return FALSE unconditionally.
\r
2913 BOOL IsFullRepaintPreferrable()
\r
2915 BOOL result = FALSE;
\r
2917 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2918 /* Arrow may appear on the board */
\r
2926 This function is called by DrawPosition to know whether a full repaint must
\r
2929 Only DrawPosition may directly call this function, which makes use of
\r
2930 some state information. Other function should call DrawPosition specifying
\r
2931 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2933 BOOL DrawPositionNeedsFullRepaint()
\r
2935 BOOL result = FALSE;
\r
2938 Probably a slightly better policy would be to trigger a full repaint
\r
2939 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2940 but animation is fast enough that it's difficult to notice.
\r
2942 if( animInfo.piece == EmptySquare ) {
\r
2943 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2952 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2954 int row, column, x, y, square_color, piece_color;
\r
2955 ChessSquare piece;
\r
2957 HDC texture_hdc = NULL;
\r
2959 /* [AS] Initialize background textures if needed */
\r
2960 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2961 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2962 if( backTextureSquareSize != squareSize
\r
2963 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2964 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2965 backTextureSquareSize = squareSize;
\r
2966 RebuildTextureSquareInfo();
\r
2969 texture_hdc = CreateCompatibleDC( hdc );
\r
2972 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2973 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2975 SquareToPos(row, column, &x, &y);
\r
2977 piece = board[row][column];
\r
2979 square_color = ((column + row) % 2) == 1;
\r
2980 if( gameInfo.variant == VariantXiangqi ) {
\r
2981 square_color = !InPalace(row, column);
\r
2982 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2983 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2985 piece_color = (int) piece < (int) BlackPawn;
\r
2988 /* [HGM] holdings file: light square or black */
\r
2989 if(column == BOARD_LEFT-2) {
\r
2990 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2993 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
2997 if(column == BOARD_RGHT + 1 ) {
\r
2998 if( row < gameInfo.holdingsSize )
\r
3001 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3005 if(column == BOARD_LEFT-1 ) /* left align */
\r
3006 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3007 else if( column == BOARD_RGHT) /* right align */
\r
3008 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3010 if (appData.monoMode) {
\r
3011 if (piece == EmptySquare) {
\r
3012 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3013 square_color ? WHITENESS : BLACKNESS);
\r
3015 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3018 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3019 /* [AS] Draw the square using a texture bitmap */
\r
3020 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3021 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3022 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3025 squareSize, squareSize,
\r
3028 backTextureSquareInfo[r][c].mode,
\r
3029 backTextureSquareInfo[r][c].x,
\r
3030 backTextureSquareInfo[r][c].y );
\r
3032 SelectObject( texture_hdc, hbm );
\r
3034 if (piece != EmptySquare) {
\r
3035 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3039 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3041 oldBrush = SelectObject(hdc, brush );
\r
3042 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3043 SelectObject(hdc, oldBrush);
\r
3044 if (piece != EmptySquare)
\r
3045 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3050 if( texture_hdc != NULL ) {
\r
3051 DeleteDC( texture_hdc );
\r
3055 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3056 void fputDW(FILE *f, int x)
\r
3058 fputc(x & 255, f);
\r
3059 fputc(x>>8 & 255, f);
\r
3060 fputc(x>>16 & 255, f);
\r
3061 fputc(x>>24 & 255, f);
\r
3064 #define MAX_CLIPS 200 /* more than enough */
\r
3067 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3069 // HBITMAP bufferBitmap;
\r
3074 int w = 100, h = 50;
\r
3076 if(logo == NULL) return;
\r
3077 // GetClientRect(hwndMain, &Rect);
\r
3078 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3079 // Rect.bottom-Rect.top+1);
\r
3080 tmphdc = CreateCompatibleDC(hdc);
\r
3081 hbm = SelectObject(tmphdc, logo);
\r
3082 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3086 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3087 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3088 SelectObject(tmphdc, hbm);
\r
3092 static HDC hdcSeek;
\r
3094 // [HGM] seekgraph
\r
3095 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3098 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3099 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3100 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3101 SelectObject( hdcSeek, hp );
\r
3104 // front-end wrapper for drawing functions to do rectangles
\r
3105 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3110 if (hdcSeek == NULL) {
\r
3111 hdcSeek = GetDC(hwndMain);
\r
3112 if (!appData.monoMode) {
\r
3113 SelectPalette(hdcSeek, hPal, FALSE);
\r
3114 RealizePalette(hdcSeek);
\r
3117 hp = SelectObject( hdcSeek, gridPen );
\r
3118 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3119 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3120 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3121 SelectObject( hdcSeek, hp );
\r
3124 // front-end wrapper for putting text in graph
\r
3125 void DrawSeekText(char *buf, int x, int y)
\r
3128 SetBkMode( hdcSeek, TRANSPARENT );
\r
3129 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3130 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3133 void DrawSeekDot(int x, int y, int color)
\r
3135 int square = color & 0x80;
\r
3137 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3138 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3140 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3141 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3143 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3144 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3145 SelectObject(hdcSeek, oldBrush);
\r
3149 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3151 static Board lastReq[2], lastDrawn[2];
\r
3152 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3153 static int lastDrawnFlipView = 0;
\r
3154 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3155 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3158 HBITMAP bufferBitmap;
\r
3159 HBITMAP oldBitmap;
\r
3161 HRGN clips[MAX_CLIPS];
\r
3162 ChessSquare dragged_piece = EmptySquare;
\r
3163 int nr = twoBoards*partnerUp;
\r
3165 /* I'm undecided on this - this function figures out whether a full
\r
3166 * repaint is necessary on its own, so there's no real reason to have the
\r
3167 * caller tell it that. I think this can safely be set to FALSE - but
\r
3168 * if we trust the callers not to request full repaints unnessesarily, then
\r
3169 * we could skip some clipping work. In other words, only request a full
\r
3170 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3171 * gamestart and similar) --Hawk
\r
3173 Boolean fullrepaint = repaint;
\r
3175 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3177 if( DrawPositionNeedsFullRepaint() ) {
\r
3178 fullrepaint = TRUE;
\r
3181 if (board == NULL) {
\r
3182 if (!lastReqValid[nr]) {
\r
3185 board = lastReq[nr];
\r
3187 CopyBoard(lastReq[nr], board);
\r
3188 lastReqValid[nr] = 1;
\r
3191 if (doingSizing) {
\r
3195 if (IsIconic(hwndMain)) {
\r
3199 if (hdc == NULL) {
\r
3200 hdc = GetDC(hwndMain);
\r
3201 if (!appData.monoMode) {
\r
3202 SelectPalette(hdc, hPal, FALSE);
\r
3203 RealizePalette(hdc);
\r
3207 releaseDC = FALSE;
\r
3210 /* Create some work-DCs */
\r
3211 hdcmem = CreateCompatibleDC(hdc);
\r
3212 tmphdc = CreateCompatibleDC(hdc);
\r
3214 /* If dragging is in progress, we temporarely remove the piece */
\r
3215 /* [HGM] or temporarily decrease count if stacked */
\r
3216 /* !! Moved to before board compare !! */
\r
3217 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3218 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3219 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3220 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3221 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3223 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3224 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3225 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3227 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3230 /* Figure out which squares need updating by comparing the
\r
3231 * newest board with the last drawn board and checking if
\r
3232 * flipping has changed.
\r
3234 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3235 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3236 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3237 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3238 SquareToPos(row, column, &x, &y);
\r
3239 clips[num_clips++] =
\r
3240 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3244 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3245 for (i=0; i<2; i++) {
\r
3246 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3247 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3248 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3249 lastDrawnHighlight.sq[i].y >= 0) {
\r
3250 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3251 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3252 clips[num_clips++] =
\r
3253 CreateRectRgn(x - lineGap, y - lineGap,
\r
3254 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3256 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3257 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3258 clips[num_clips++] =
\r
3259 CreateRectRgn(x - lineGap, y - lineGap,
\r
3260 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3264 for (i=0; i<2; i++) {
\r
3265 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3266 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3267 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3268 lastDrawnPremove.sq[i].y >= 0) {
\r
3269 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3270 lastDrawnPremove.sq[i].x, &x, &y);
\r
3271 clips[num_clips++] =
\r
3272 CreateRectRgn(x - lineGap, y - lineGap,
\r
3273 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3275 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3276 premoveHighlightInfo.sq[i].y >= 0) {
\r
3277 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3278 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3279 clips[num_clips++] =
\r
3280 CreateRectRgn(x - lineGap, y - lineGap,
\r
3281 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3285 } else { // nr == 1
\r
3286 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3287 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3288 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3289 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3290 for (i=0; i<2; i++) {
\r
3291 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3292 partnerHighlightInfo.sq[i].y >= 0) {
\r
3293 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3294 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3295 clips[num_clips++] =
\r
3296 CreateRectRgn(x - lineGap, y - lineGap,
\r
3297 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3299 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3300 oldPartnerHighlight.sq[i].y >= 0) {
\r
3301 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3302 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3303 clips[num_clips++] =
\r
3304 CreateRectRgn(x - lineGap, y - lineGap,
\r
3305 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3310 fullrepaint = TRUE;
\r
3313 /* Create a buffer bitmap - this is the actual bitmap
\r
3314 * being written to. When all the work is done, we can
\r
3315 * copy it to the real DC (the screen). This avoids
\r
3316 * the problems with flickering.
\r
3318 GetClientRect(hwndMain, &Rect);
\r
3319 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3320 Rect.bottom-Rect.top+1);
\r
3321 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3322 if (!appData.monoMode) {
\r
3323 SelectPalette(hdcmem, hPal, FALSE);
\r
3326 /* Create clips for dragging */
\r
3327 if (!fullrepaint) {
\r
3328 if (dragInfo.from.x >= 0) {
\r
3329 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3330 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3332 if (dragInfo.start.x >= 0) {
\r
3333 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3334 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3336 if (dragInfo.pos.x >= 0) {
\r
3337 x = dragInfo.pos.x - squareSize / 2;
\r
3338 y = dragInfo.pos.y - squareSize / 2;
\r
3339 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3341 if (dragInfo.lastpos.x >= 0) {
\r
3342 x = dragInfo.lastpos.x - squareSize / 2;
\r
3343 y = dragInfo.lastpos.y - squareSize / 2;
\r
3344 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3348 /* Are we animating a move?
\r
3350 * - remove the piece from the board (temporarely)
\r
3351 * - calculate the clipping region
\r
3353 if (!fullrepaint) {
\r
3354 if (animInfo.piece != EmptySquare) {
\r
3355 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3356 x = boardRect.left + animInfo.lastpos.x;
\r
3357 y = boardRect.top + animInfo.lastpos.y;
\r
3358 x2 = boardRect.left + animInfo.pos.x;
\r
3359 y2 = boardRect.top + animInfo.pos.y;
\r
3360 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3361 /* Slight kludge. The real problem is that after AnimateMove is
\r
3362 done, the position on the screen does not match lastDrawn.
\r
3363 This currently causes trouble only on e.p. captures in
\r
3364 atomic, where the piece moves to an empty square and then
\r
3365 explodes. The old and new positions both had an empty square
\r
3366 at the destination, but animation has drawn a piece there and
\r
3367 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3368 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3372 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3373 if (num_clips == 0)
\r
3374 fullrepaint = TRUE;
\r
3376 /* Set clipping on the memory DC */
\r
3377 if (!fullrepaint) {
\r
3378 SelectClipRgn(hdcmem, clips[0]);
\r
3379 for (x = 1; x < num_clips; x++) {
\r
3380 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3381 abort(); // this should never ever happen!
\r
3385 /* Do all the drawing to the memory DC */
\r
3386 if(explodeInfo.radius) { // [HGM] atomic
\r
3388 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3389 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3390 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3391 x += squareSize/2;
\r
3392 y += squareSize/2;
\r
3393 if(!fullrepaint) {
\r
3394 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3395 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3397 DrawGridOnDC(hdcmem);
\r
3398 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3399 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3400 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3401 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3402 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3403 SelectObject(hdcmem, oldBrush);
\r
3405 DrawGridOnDC(hdcmem);
\r
3406 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3407 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3408 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3410 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3411 oldPartnerHighlight = partnerHighlightInfo;
\r
3413 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3415 if(nr == 0) // [HGM] dual: markers only on left board
\r
3416 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3417 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3418 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3419 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3420 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3421 SquareToPos(row, column, &x, &y);
\r
3422 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3423 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3424 SelectObject(hdcmem, oldBrush);
\r
3429 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3430 if(appData.autoLogo) {
\r
3432 switch(gameMode) { // pick logos based on game mode
\r
3433 case IcsObserving:
\r
3434 whiteLogo = second.programLogo; // ICS logo
\r
3435 blackLogo = second.programLogo;
\r
3438 case IcsPlayingWhite:
\r
3439 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3440 blackLogo = second.programLogo; // ICS logo
\r
3442 case IcsPlayingBlack:
\r
3443 whiteLogo = second.programLogo; // ICS logo
\r
3444 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3446 case TwoMachinesPlay:
\r
3447 if(first.twoMachinesColor[0] == 'b') {
\r
3448 whiteLogo = second.programLogo;
\r
3449 blackLogo = first.programLogo;
\r
3452 case MachinePlaysWhite:
\r
3453 blackLogo = userLogo;
\r
3455 case MachinePlaysBlack:
\r
3456 whiteLogo = userLogo;
\r
3457 blackLogo = first.programLogo;
\r
3460 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3461 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3464 if( appData.highlightMoveWithArrow ) {
\r
3465 DrawArrowHighlight(hdcmem);
\r
3468 DrawCoordsOnDC(hdcmem);
\r
3470 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3471 /* to make sure lastDrawn contains what is actually drawn */
\r
3473 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3474 if (dragged_piece != EmptySquare) {
\r
3475 /* [HGM] or restack */
\r
3476 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3477 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3479 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3480 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3481 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3482 x = dragInfo.pos.x - squareSize / 2;
\r
3483 y = dragInfo.pos.y - squareSize / 2;
\r
3484 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3485 ((int) dragged_piece < (int) BlackPawn),
\r
3486 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3489 /* Put the animated piece back into place and draw it */
\r
3490 if (animInfo.piece != EmptySquare) {
\r
3491 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3492 x = boardRect.left + animInfo.pos.x;
\r
3493 y = boardRect.top + animInfo.pos.y;
\r
3494 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3495 ((int) animInfo.piece < (int) BlackPawn),
\r
3496 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3499 /* Release the bufferBitmap by selecting in the old bitmap
\r
3500 * and delete the memory DC
\r
3502 SelectObject(hdcmem, oldBitmap);
\r
3505 /* Set clipping on the target DC */
\r
3506 if (!fullrepaint) {
\r
3507 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3509 GetRgnBox(clips[x], &rect);
\r
3510 DeleteObject(clips[x]);
\r
3511 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3512 rect.right + wpMain.width/2, rect.bottom);
\r
3514 SelectClipRgn(hdc, clips[0]);
\r
3515 for (x = 1; x < num_clips; x++) {
\r
3516 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3517 abort(); // this should never ever happen!
\r
3521 /* Copy the new bitmap onto the screen in one go.
\r
3522 * This way we avoid any flickering
\r
3524 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3525 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3526 boardRect.right - boardRect.left,
\r
3527 boardRect.bottom - boardRect.top,
\r
3528 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3529 if(saveDiagFlag) {
\r
3530 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3531 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3533 GetObject(bufferBitmap, sizeof(b), &b);
\r
3534 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3535 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3536 bih.biWidth = b.bmWidth;
\r
3537 bih.biHeight = b.bmHeight;
\r
3539 bih.biBitCount = b.bmBitsPixel;
\r
3540 bih.biCompression = 0;
\r
3541 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3542 bih.biXPelsPerMeter = 0;
\r
3543 bih.biYPelsPerMeter = 0;
\r
3544 bih.biClrUsed = 0;
\r
3545 bih.biClrImportant = 0;
\r
3546 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3547 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3548 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3549 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3551 wb = b.bmWidthBytes;
\r
3553 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3554 int k = ((int*) pData)[i];
\r
3555 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3556 if(j >= 16) break;
\r
3558 if(j >= nrColors) nrColors = j+1;
\r
3560 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3562 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3563 for(w=0; w<(wb>>2); w+=2) {
\r
3564 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3565 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3566 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3567 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3568 pData[p++] = m | j<<4;
\r
3570 while(p&3) pData[p++] = 0;
\r
3573 wb = ((wb+31)>>5)<<2;
\r
3575 // write BITMAPFILEHEADER
\r
3576 fprintf(diagFile, "BM");
\r
3577 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3578 fputDW(diagFile, 0);
\r
3579 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3580 // write BITMAPINFOHEADER
\r
3581 fputDW(diagFile, 40);
\r
3582 fputDW(diagFile, b.bmWidth);
\r
3583 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3584 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3585 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3586 fputDW(diagFile, 0);
\r
3587 fputDW(diagFile, 0);
\r
3588 fputDW(diagFile, 0);
\r
3589 fputDW(diagFile, 0);
\r
3590 fputDW(diagFile, 0);
\r
3591 fputDW(diagFile, 0);
\r
3592 // write color table
\r
3594 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3595 // write bitmap data
\r
3596 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3597 fputc(pData[i], diagFile);
\r
3601 SelectObject(tmphdc, oldBitmap);
\r
3603 /* Massive cleanup */
\r
3604 for (x = 0; x < num_clips; x++)
\r
3605 DeleteObject(clips[x]);
\r
3608 DeleteObject(bufferBitmap);
\r
3611 ReleaseDC(hwndMain, hdc);
\r
3613 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3615 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3617 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3620 /* CopyBoard(lastDrawn, board);*/
\r
3621 lastDrawnHighlight = highlightInfo;
\r
3622 lastDrawnPremove = premoveHighlightInfo;
\r
3623 lastDrawnFlipView = flipView;
\r
3624 lastDrawnValid[nr] = 1;
\r
3627 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3632 saveDiagFlag = 1; diagFile = f;
\r
3633 HDCDrawPosition(NULL, TRUE, NULL);
\r
3637 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3644 /*---------------------------------------------------------------------------*\
\r
3645 | CLIENT PAINT PROCEDURE
\r
3646 | This is the main event-handler for the WM_PAINT message.
\r
3648 \*---------------------------------------------------------------------------*/
\r
3650 PaintProc(HWND hwnd)
\r
3656 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3657 if (IsIconic(hwnd)) {
\r
3658 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3660 if (!appData.monoMode) {
\r
3661 SelectPalette(hdc, hPal, FALSE);
\r
3662 RealizePalette(hdc);
\r
3664 HDCDrawPosition(hdc, 1, NULL);
\r
3665 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3666 flipView = !flipView; partnerUp = !partnerUp;
\r
3667 HDCDrawPosition(hdc, 1, NULL);
\r
3668 flipView = !flipView; partnerUp = !partnerUp;
\r
3671 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3672 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3673 ETO_CLIPPED|ETO_OPAQUE,
\r
3674 &messageRect, messageText, strlen(messageText), NULL);
\r
3675 SelectObject(hdc, oldFont);
\r
3676 DisplayBothClocks();
\r
3678 EndPaint(hwnd,&ps);
\r
3686 * If the user selects on a border boundary, return -1; if off the board,
\r
3687 * return -2. Otherwise map the event coordinate to the square.
\r
3688 * The offset boardRect.left or boardRect.top must already have been
\r
3689 * subtracted from x.
\r
3691 int EventToSquare(x, limit)
\r
3699 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3701 x /= (squareSize + lineGap);
\r
3713 DropEnable dropEnables[] = {
\r
3714 { 'P', DP_Pawn, "Pawn" },
\r
3715 { 'N', DP_Knight, "Knight" },
\r
3716 { 'B', DP_Bishop, "Bishop" },
\r
3717 { 'R', DP_Rook, "Rook" },
\r
3718 { 'Q', DP_Queen, "Queen" },
\r
3722 SetupDropMenu(HMENU hmenu)
\r
3724 int i, count, enable;
\r
3726 extern char white_holding[], black_holding[];
\r
3727 char item[MSG_SIZ];
\r
3729 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3730 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3731 dropEnables[i].piece);
\r
3733 while (p && *p++ == dropEnables[i].piece) count++;
\r
3734 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3735 enable = count > 0 || !appData.testLegality
\r
3736 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3737 && !appData.icsActive);
\r
3738 ModifyMenu(hmenu, dropEnables[i].command,
\r
3739 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3740 dropEnables[i].command, item);
\r
3744 void DragPieceBegin(int x, int y)
\r
3746 dragInfo.lastpos.x = boardRect.left + x;
\r
3747 dragInfo.lastpos.y = boardRect.top + y;
\r
3748 dragInfo.from.x = fromX;
\r
3749 dragInfo.from.y = fromY;
\r
3750 dragInfo.start = dragInfo.from;
\r
3751 SetCapture(hwndMain);
\r
3754 void DragPieceEnd(int x, int y)
\r
3757 dragInfo.start.x = dragInfo.start.y = -1;
\r
3758 dragInfo.from = dragInfo.start;
\r
3759 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3762 /* Event handler for mouse messages */
\r
3764 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3768 static int recursive = 0;
\r
3770 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3773 if (message == WM_MBUTTONUP) {
\r
3774 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3775 to the middle button: we simulate pressing the left button too!
\r
3777 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3778 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3784 pt.x = LOWORD(lParam);
\r
3785 pt.y = HIWORD(lParam);
\r
3786 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3787 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3788 if (!flipView && y >= 0) {
\r
3789 y = BOARD_HEIGHT - 1 - y;
\r
3791 if (flipView && x >= 0) {
\r
3792 x = BOARD_WIDTH - 1 - x;
\r
3795 switch (message) {
\r
3796 case WM_LBUTTONDOWN:
\r
3797 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3798 if (gameMode == EditPosition) {
\r
3799 SetWhiteToPlayEvent();
\r
3800 } else if (gameMode == IcsPlayingBlack ||
\r
3801 gameMode == MachinePlaysWhite) {
\r
3803 } else if (gameMode == EditGame) {
\r
3804 AdjustClock(flipClock, -1);
\r
3806 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3807 if (gameMode == EditPosition) {
\r
3808 SetBlackToPlayEvent();
\r
3809 } else if (gameMode == IcsPlayingWhite ||
\r
3810 gameMode == MachinePlaysBlack) {
\r
3812 } else if (gameMode == EditGame) {
\r
3813 AdjustClock(!flipClock, -1);
\r
3816 dragInfo.start.x = dragInfo.start.y = -1;
\r
3817 dragInfo.from = dragInfo.start;
\r
3818 if(fromX == -1 && frozen) { // not sure where this is for
\r
3819 fromX = fromY = -1;
\r
3820 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3823 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3824 DrawPosition(TRUE, NULL);
\r
3827 case WM_LBUTTONUP:
\r
3828 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3829 DrawPosition(TRUE, NULL);
\r
3832 case WM_MOUSEMOVE:
\r
3833 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3834 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3835 if ((appData.animateDragging || appData.highlightDragging)
\r
3836 && (wParam & MK_LBUTTON)
\r
3837 && dragInfo.from.x >= 0)
\r
3839 BOOL full_repaint = FALSE;
\r
3841 if (appData.animateDragging) {
\r
3842 dragInfo.pos = pt;
\r
3844 if (appData.highlightDragging) {
\r
3845 SetHighlights(fromX, fromY, x, y);
\r
3846 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3847 full_repaint = TRUE;
\r
3851 DrawPosition( full_repaint, NULL);
\r
3853 dragInfo.lastpos = dragInfo.pos;
\r
3857 case WM_MOUSEWHEEL: // [DM]
\r
3858 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3859 /* Mouse Wheel is being rolled forward
\r
3860 * Play moves forward
\r
3862 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3863 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3864 /* Mouse Wheel is being rolled backward
\r
3865 * Play moves backward
\r
3867 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3868 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3872 case WM_MBUTTONUP:
\r
3873 case WM_RBUTTONUP:
\r
3875 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3878 case WM_MBUTTONDOWN:
\r
3879 case WM_RBUTTONDOWN:
\r
3882 fromX = fromY = -1;
\r
3883 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3884 dragInfo.start.x = dragInfo.start.y = -1;
\r
3885 dragInfo.from = dragInfo.start;
\r
3886 dragInfo.lastpos = dragInfo.pos;
\r
3887 if (appData.highlightDragging) {
\r
3888 ClearHighlights();
\r
3891 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3892 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3893 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3894 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3895 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3898 DrawPosition(TRUE, NULL);
\r
3900 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3903 if (message == WM_MBUTTONDOWN) {
\r
3904 buttonCount = 3; /* even if system didn't think so */
\r
3905 if (wParam & MK_SHIFT)
\r
3906 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3908 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3909 } else { /* message == WM_RBUTTONDOWN */
\r
3910 /* Just have one menu, on the right button. Windows users don't
\r
3911 think to try the middle one, and sometimes other software steals
\r
3912 it, or it doesn't really exist. */
\r
3913 if(gameInfo.variant != VariantShogi)
\r
3914 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3916 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3920 SetCapture(hwndMain);
3923 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3924 SetupDropMenu(hmenu);
\r
3925 MenuPopup(hwnd, pt, hmenu, -1);
\r
3935 /* Preprocess messages for buttons in main window */
\r
3937 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3939 int id = GetWindowLong(hwnd, GWL_ID);
\r
3942 for (i=0; i<N_BUTTONS; i++) {
\r
3943 if (buttonDesc[i].id == id) break;
\r
3945 if (i == N_BUTTONS) return 0;
\r
3946 switch (message) {
\r
3951 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3952 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3959 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3962 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3963 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3964 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3965 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3967 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3969 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3970 PopUpMoveDialog((char)wParam);
\r
3976 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3979 /* Process messages for Promotion dialog box */
\r
3981 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3985 switch (message) {
\r
3986 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3987 /* Center the dialog over the application window */
\r
3988 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3989 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3990 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3991 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3992 SW_SHOW : SW_HIDE);
\r
3993 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3994 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3995 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3996 PieceToChar(WhiteAngel) != '~') ||
\r
3997 (PieceToChar(BlackAngel) >= 'A' &&
\r
3998 PieceToChar(BlackAngel) != '~') ) ?
\r
3999 SW_SHOW : SW_HIDE);
\r
4000 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4001 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4002 PieceToChar(WhiteMarshall) != '~') ||
\r
4003 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4004 PieceToChar(BlackMarshall) != '~') ) ?
\r
4005 SW_SHOW : SW_HIDE);
\r
4006 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4007 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4008 gameInfo.variant != VariantShogi ?
\r
4009 SW_SHOW : SW_HIDE);
\r
4010 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4011 gameInfo.variant != VariantShogi ?
\r
4012 SW_SHOW : SW_HIDE);
\r
4013 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4014 gameInfo.variant == VariantShogi ?
\r
4015 SW_SHOW : SW_HIDE);
\r
4016 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4017 gameInfo.variant == VariantShogi ?
\r
4018 SW_SHOW : SW_HIDE);
\r
4019 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4020 gameInfo.variant == VariantSuper ?
\r
4021 SW_SHOW : SW_HIDE);
\r
4024 case WM_COMMAND: /* message: received a command */
\r
4025 switch (LOWORD(wParam)) {
\r
4027 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4028 ClearHighlights();
\r
4029 DrawPosition(FALSE, NULL);
\r
4032 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4035 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4038 promoChar = PieceToChar(BlackRook);
\r
4041 promoChar = PieceToChar(BlackBishop);
\r
4043 case PB_Chancellor:
\r
4044 promoChar = PieceToChar(BlackMarshall);
\r
4046 case PB_Archbishop:
\r
4047 promoChar = PieceToChar(BlackAngel);
\r
4050 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4055 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4056 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4057 only show the popup when we are already sure the move is valid or
\r
4058 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4059 will figure out it is a promotion from the promoChar. */
\r
4060 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4061 fromX = fromY = -1;
\r
4062 if (!appData.highlightLastMove) {
\r
4063 ClearHighlights();
\r
4064 DrawPosition(FALSE, NULL);
\r
4071 /* Pop up promotion dialog */
\r
4073 PromotionPopup(HWND hwnd)
\r
4077 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4078 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4079 hwnd, (DLGPROC)lpProc);
\r
4080 FreeProcInstance(lpProc);
\r
4086 DrawPosition(TRUE, NULL);
\r
4087 PromotionPopup(hwndMain);
\r
4090 /* Toggle ShowThinking */
\r
4092 ToggleShowThinking()
\r
4094 appData.showThinking = !appData.showThinking;
\r
4095 ShowThinkingEvent();
\r
4099 LoadGameDialog(HWND hwnd, char* title)
\r
4103 char fileTitle[MSG_SIZ];
\r
4104 f = OpenFileDialog(hwnd, "rb", "",
\r
4105 appData.oldSaveStyle ? "gam" : "pgn",
\r
4107 title, &number, fileTitle, NULL);
\r
4109 cmailMsgLoaded = FALSE;
\r
4110 if (number == 0) {
\r
4111 int error = GameListBuild(f);
\r
4113 DisplayError("Cannot build game list", error);
\r
4114 } else if (!ListEmpty(&gameList) &&
\r
4115 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4116 GameListPopUp(f, fileTitle);
\r
4119 GameListDestroy();
\r
4122 LoadGame(f, number, fileTitle, FALSE);
\r
4126 int get_term_width()
\r
4131 HFONT hfont, hold_font;
\r
4136 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4140 // get the text metrics
\r
4141 hdc = GetDC(hText);
\r
4142 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4143 if (consoleCF.dwEffects & CFE_BOLD)
\r
4144 lf.lfWeight = FW_BOLD;
\r
4145 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4146 lf.lfItalic = TRUE;
\r
4147 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4148 lf.lfStrikeOut = TRUE;
\r
4149 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4150 lf.lfUnderline = TRUE;
\r
4151 hfont = CreateFontIndirect(&lf);
\r
4152 hold_font = SelectObject(hdc, hfont);
\r
4153 GetTextMetrics(hdc, &tm);
\r
4154 SelectObject(hdc, hold_font);
\r
4155 DeleteObject(hfont);
\r
4156 ReleaseDC(hText, hdc);
\r
4158 // get the rectangle
\r
4159 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4161 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4164 void UpdateICSWidth(HWND hText)
\r
4166 LONG old_width, new_width;
\r
4168 new_width = get_term_width(hText, FALSE);
\r
4169 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4170 if (new_width != old_width)
\r
4172 ics_update_width(new_width);
\r
4173 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4178 ChangedConsoleFont()
\r
4181 CHARRANGE tmpsel, sel;
\r
4182 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4183 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4184 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4187 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4188 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4189 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4190 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4191 * size. This was undocumented in the version of MSVC++ that I had
\r
4192 * when I wrote the code, but is apparently documented now.
\r
4194 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4195 cfmt.bCharSet = f->lf.lfCharSet;
\r
4196 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4197 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4198 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4199 /* Why are the following seemingly needed too? */
\r
4200 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4201 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4202 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4204 tmpsel.cpMax = -1; /*999999?*/
\r
4205 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4206 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4207 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4208 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4210 paraf.cbSize = sizeof(paraf);
\r
4211 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4212 paraf.dxStartIndent = 0;
\r
4213 paraf.dxOffset = WRAP_INDENT;
\r
4214 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4215 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4216 UpdateICSWidth(hText);
\r
4219 /*---------------------------------------------------------------------------*\
\r
4221 * Window Proc for main window
\r
4223 \*---------------------------------------------------------------------------*/
\r
4225 /* Process messages for main window, etc. */
\r
4227 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4230 int wmId, wmEvent;
\r
4234 char fileTitle[MSG_SIZ];
\r
4235 char buf[MSG_SIZ];
\r
4236 static SnapData sd;
\r
4238 switch (message) {
\r
4240 case WM_PAINT: /* message: repaint portion of window */
\r
4244 case WM_ERASEBKGND:
\r
4245 if (IsIconic(hwnd)) {
\r
4246 /* Cheat; change the message */
\r
4247 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4249 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4253 case WM_LBUTTONDOWN:
\r
4254 case WM_MBUTTONDOWN:
\r
4255 case WM_RBUTTONDOWN:
\r
4256 case WM_LBUTTONUP:
\r
4257 case WM_MBUTTONUP:
\r
4258 case WM_RBUTTONUP:
\r
4259 case WM_MOUSEMOVE:
\r
4260 case WM_MOUSEWHEEL:
\r
4261 MouseEvent(hwnd, message, wParam, lParam);
\r
4264 JAWS_KB_NAVIGATION
\r
4268 JAWS_ALT_INTERCEPT
\r
4270 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4271 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4272 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4273 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4275 SendMessage(h, message, wParam, lParam);
\r
4276 } else if(lParam != KF_REPEAT) {
\r
4277 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4278 PopUpMoveDialog((char)wParam);
\r
4279 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4280 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4285 case WM_PALETTECHANGED:
\r
4286 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4288 HDC hdc = GetDC(hwndMain);
\r
4289 SelectPalette(hdc, hPal, TRUE);
\r
4290 nnew = RealizePalette(hdc);
\r
4292 paletteChanged = TRUE;
\r
4293 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4295 ReleaseDC(hwnd, hdc);
\r
4299 case WM_QUERYNEWPALETTE:
\r
4300 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4302 HDC hdc = GetDC(hwndMain);
\r
4303 paletteChanged = FALSE;
\r
4304 SelectPalette(hdc, hPal, FALSE);
\r
4305 nnew = RealizePalette(hdc);
\r
4307 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4309 ReleaseDC(hwnd, hdc);
\r
4314 case WM_COMMAND: /* message: command from application menu */
\r
4315 wmId = LOWORD(wParam);
\r
4316 wmEvent = HIWORD(wParam);
\r
4321 SAY("new game enter a move to play against the computer with white");
\r
4324 case IDM_NewGameFRC:
\r
4325 if( NewGameFRC() == 0 ) {
\r
4330 case IDM_NewVariant:
\r
4331 NewVariantPopup(hwnd);
\r
4334 case IDM_LoadGame:
\r
4335 LoadGameDialog(hwnd, "Load Game from File");
\r
4338 case IDM_LoadNextGame:
\r
4342 case IDM_LoadPrevGame:
\r
4346 case IDM_ReloadGame:
\r
4350 case IDM_LoadPosition:
\r
4351 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4352 Reset(FALSE, TRUE);
\r
4355 f = OpenFileDialog(hwnd, "rb", "",
\r
4356 appData.oldSaveStyle ? "pos" : "fen",
\r
4358 "Load Position from File", &number, fileTitle, NULL);
\r
4360 LoadPosition(f, number, fileTitle);
\r
4364 case IDM_LoadNextPosition:
\r
4365 ReloadPosition(1);
\r
4368 case IDM_LoadPrevPosition:
\r
4369 ReloadPosition(-1);
\r
4372 case IDM_ReloadPosition:
\r
4373 ReloadPosition(0);
\r
4376 case IDM_SaveGame:
\r
4377 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4378 f = OpenFileDialog(hwnd, "a", defName,
\r
4379 appData.oldSaveStyle ? "gam" : "pgn",
\r
4381 "Save Game to File", NULL, fileTitle, NULL);
\r
4383 SaveGame(f, 0, "");
\r
4387 case IDM_SavePosition:
\r
4388 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4389 f = OpenFileDialog(hwnd, "a", defName,
\r
4390 appData.oldSaveStyle ? "pos" : "fen",
\r
4392 "Save Position to File", NULL, fileTitle, NULL);
\r
4394 SavePosition(f, 0, "");
\r
4398 case IDM_SaveDiagram:
\r
4399 defName = "diagram";
\r
4400 f = OpenFileDialog(hwnd, "wb", defName,
\r
4403 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4409 case IDM_CopyGame:
\r
4410 CopyGameToClipboard();
\r
4413 case IDM_PasteGame:
\r
4414 PasteGameFromClipboard();
\r
4417 case IDM_CopyGameListToClipboard:
\r
4418 CopyGameListToClipboard();
\r
4421 /* [AS] Autodetect FEN or PGN data */
\r
4422 case IDM_PasteAny:
\r
4423 PasteGameOrFENFromClipboard();
\r
4426 /* [AS] Move history */
\r
4427 case IDM_ShowMoveHistory:
\r
4428 if( MoveHistoryIsUp() ) {
\r
4429 MoveHistoryPopDown();
\r
4432 MoveHistoryPopUp();
\r
4436 /* [AS] Eval graph */
\r
4437 case IDM_ShowEvalGraph:
\r
4438 if( EvalGraphIsUp() ) {
\r
4439 EvalGraphPopDown();
\r
4443 SetFocus(hwndMain);
\r
4447 /* [AS] Engine output */
\r
4448 case IDM_ShowEngineOutput:
\r
4449 if( EngineOutputIsUp() ) {
\r
4450 EngineOutputPopDown();
\r
4453 EngineOutputPopUp();
\r
4457 /* [AS] User adjudication */
\r
4458 case IDM_UserAdjudication_White:
\r
4459 UserAdjudicationEvent( +1 );
\r
4462 case IDM_UserAdjudication_Black:
\r
4463 UserAdjudicationEvent( -1 );
\r
4466 case IDM_UserAdjudication_Draw:
\r
4467 UserAdjudicationEvent( 0 );
\r
4470 /* [AS] Game list options dialog */
\r
4471 case IDM_GameListOptions:
\r
4472 GameListOptions();
\r
4479 case IDM_CopyPosition:
\r
4480 CopyFENToClipboard();
\r
4483 case IDM_PastePosition:
\r
4484 PasteFENFromClipboard();
\r
4487 case IDM_MailMove:
\r
4491 case IDM_ReloadCMailMsg:
\r
4492 Reset(TRUE, TRUE);
\r
4493 ReloadCmailMsgEvent(FALSE);
\r
4496 case IDM_Minimize:
\r
4497 ShowWindow(hwnd, SW_MINIMIZE);
\r
4504 case IDM_MachineWhite:
\r
4505 MachineWhiteEvent();
\r
4507 * refresh the tags dialog only if it's visible
\r
4509 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4511 tags = PGNTags(&gameInfo);
\r
4512 TagsPopUp(tags, CmailMsg());
\r
4515 SAY("computer starts playing white");
\r
4518 case IDM_MachineBlack:
\r
4519 MachineBlackEvent();
\r
4521 * refresh the tags dialog only if it's visible
\r
4523 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4525 tags = PGNTags(&gameInfo);
\r
4526 TagsPopUp(tags, CmailMsg());
\r
4529 SAY("computer starts playing black");
\r
4532 case IDM_TwoMachines:
\r
4533 TwoMachinesEvent();
\r
4535 * refresh the tags dialog only if it's visible
\r
4537 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4539 tags = PGNTags(&gameInfo);
\r
4540 TagsPopUp(tags, CmailMsg());
\r
4543 SAY("programs start playing each other");
\r
4546 case IDM_AnalysisMode:
\r
4547 if (!first.analysisSupport) {
\r
4548 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4549 DisplayError(buf, 0);
\r
4551 SAY("analyzing current position");
\r
4552 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4553 if (appData.icsActive) {
\r
4554 if (gameMode != IcsObserving) {
\r
4555 sprintf(buf, "You are not observing a game");
\r
4556 DisplayError(buf, 0);
\r
4557 /* secure check */
\r
4558 if (appData.icsEngineAnalyze) {
\r
4559 if (appData.debugMode)
\r
4560 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4561 ExitAnalyzeMode();
\r
4567 /* if enable, user want disable icsEngineAnalyze */
\r
4568 if (appData.icsEngineAnalyze) {
\r
4569 ExitAnalyzeMode();
\r
4573 appData.icsEngineAnalyze = TRUE;
\r
4574 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4577 if (!appData.showThinking) ToggleShowThinking();
\r
4578 AnalyzeModeEvent();
\r
4582 case IDM_AnalyzeFile:
\r
4583 if (!first.analysisSupport) {
\r
4584 char buf[MSG_SIZ];
\r
4585 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4586 DisplayError(buf, 0);
\r
4588 if (!appData.showThinking) ToggleShowThinking();
\r
4589 AnalyzeFileEvent();
\r
4590 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4591 AnalysisPeriodicEvent(1);
\r
4595 case IDM_IcsClient:
\r
4599 case IDM_EditGame:
\r
4604 case IDM_EditPosition:
\r
4605 EditPositionEvent();
\r
4606 SAY("to set up a position type a FEN");
\r
4609 case IDM_Training:
\r
4613 case IDM_ShowGameList:
\r
4614 ShowGameListProc();
\r
4617 case IDM_EditTags:
\r
4621 case IDM_EditComment:
\r
4622 if (commentUp && editComment) {
\r
4625 EditCommentEvent();
\r
4645 case IDM_CallFlag:
\r
4665 case IDM_StopObserving:
\r
4666 StopObservingEvent();
\r
4669 case IDM_StopExamining:
\r
4670 StopExaminingEvent();
\r
4674 UploadGameEvent();
\r
4677 case IDM_TypeInMove:
\r
4678 PopUpMoveDialog('\000');
\r
4681 case IDM_TypeInName:
\r
4682 PopUpNameDialog('\000');
\r
4685 case IDM_Backward:
\r
4687 SetFocus(hwndMain);
\r
4694 SetFocus(hwndMain);
\r
4699 SetFocus(hwndMain);
\r
4704 SetFocus(hwndMain);
\r
4708 RevertEvent(FALSE);
\r
4711 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4712 RevertEvent(TRUE);
\r
4715 case IDM_TruncateGame:
\r
4716 TruncateGameEvent();
\r
4723 case IDM_RetractMove:
\r
4724 RetractMoveEvent();
\r
4727 case IDM_FlipView:
\r
4728 flipView = !flipView;
\r
4729 DrawPosition(FALSE, NULL);
\r
4732 case IDM_FlipClock:
\r
4733 flipClock = !flipClock;
\r
4734 DisplayBothClocks();
\r
4735 DrawPosition(FALSE, NULL);
\r
4738 case IDM_MuteSounds:
\r
4739 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4740 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4741 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4744 case IDM_GeneralOptions:
\r
4745 GeneralOptionsPopup(hwnd);
\r
4746 DrawPosition(TRUE, NULL);
\r
4749 case IDM_BoardOptions:
\r
4750 BoardOptionsPopup(hwnd);
\r
4753 case IDM_EnginePlayOptions:
\r
4754 EnginePlayOptionsPopup(hwnd);
\r
4757 case IDM_Engine1Options:
\r
4758 EngineOptionsPopup(hwnd, &first);
\r
4761 case IDM_Engine2Options:
\r
4762 EngineOptionsPopup(hwnd, &second);
\r
4765 case IDM_OptionsUCI:
\r
4766 UciOptionsPopup(hwnd);
\r
4769 case IDM_IcsOptions:
\r
4770 IcsOptionsPopup(hwnd);
\r
4774 FontsOptionsPopup(hwnd);
\r
4778 SoundOptionsPopup(hwnd);
\r
4781 case IDM_CommPort:
\r
4782 CommPortOptionsPopup(hwnd);
\r
4785 case IDM_LoadOptions:
\r
4786 LoadOptionsPopup(hwnd);
\r
4789 case IDM_SaveOptions:
\r
4790 SaveOptionsPopup(hwnd);
\r
4793 case IDM_TimeControl:
\r
4794 TimeControlOptionsPopup(hwnd);
\r
4797 case IDM_SaveSettings:
\r
4798 SaveSettings(settingsFileName);
\r
4801 case IDM_SaveSettingsOnExit:
\r
4802 saveSettingsOnExit = !saveSettingsOnExit;
\r
4803 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4804 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4805 MF_CHECKED : MF_UNCHECKED));
\r
4816 case IDM_AboutGame:
\r
4821 appData.debugMode = !appData.debugMode;
\r
4822 if (appData.debugMode) {
\r
4823 char dir[MSG_SIZ];
\r
4824 GetCurrentDirectory(MSG_SIZ, dir);
\r
4825 SetCurrentDirectory(installDir);
\r
4826 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4827 SetCurrentDirectory(dir);
\r
4828 setbuf(debugFP, NULL);
\r
4835 case IDM_HELPCONTENTS:
\r
4836 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4837 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4838 MessageBox (GetFocus(),
\r
4839 "Unable to activate help",
\r
4840 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4844 case IDM_HELPSEARCH:
\r
4845 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4846 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4847 MessageBox (GetFocus(),
\r
4848 "Unable to activate help",
\r
4849 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4853 case IDM_HELPHELP:
\r
4854 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4855 MessageBox (GetFocus(),
\r
4856 "Unable to activate help",
\r
4857 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4862 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4864 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4865 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4866 FreeProcInstance(lpProc);
\r
4869 case IDM_DirectCommand1:
\r
4870 AskQuestionEvent("Direct Command",
\r
4871 "Send to chess program:", "", "1");
\r
4873 case IDM_DirectCommand2:
\r
4874 AskQuestionEvent("Direct Command",
\r
4875 "Send to second chess program:", "", "2");
\r
4878 case EP_WhitePawn:
\r
4879 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4880 fromX = fromY = -1;
\r
4883 case EP_WhiteKnight:
\r
4884 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4885 fromX = fromY = -1;
\r
4888 case EP_WhiteBishop:
\r
4889 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4890 fromX = fromY = -1;
\r
4893 case EP_WhiteRook:
\r
4894 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4895 fromX = fromY = -1;
\r
4898 case EP_WhiteQueen:
\r
4899 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4900 fromX = fromY = -1;
\r
4903 case EP_WhiteFerz:
\r
4904 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4905 fromX = fromY = -1;
\r
4908 case EP_WhiteWazir:
\r
4909 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4910 fromX = fromY = -1;
\r
4913 case EP_WhiteAlfil:
\r
4914 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4915 fromX = fromY = -1;
\r
4918 case EP_WhiteCannon:
\r
4919 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4920 fromX = fromY = -1;
\r
4923 case EP_WhiteCardinal:
\r
4924 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4925 fromX = fromY = -1;
\r
4928 case EP_WhiteMarshall:
\r
4929 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4930 fromX = fromY = -1;
\r
4933 case EP_WhiteKing:
\r
4934 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4935 fromX = fromY = -1;
\r
4938 case EP_BlackPawn:
\r
4939 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4940 fromX = fromY = -1;
\r
4943 case EP_BlackKnight:
\r
4944 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4945 fromX = fromY = -1;
\r
4948 case EP_BlackBishop:
\r
4949 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4950 fromX = fromY = -1;
\r
4953 case EP_BlackRook:
\r
4954 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4955 fromX = fromY = -1;
\r
4958 case EP_BlackQueen:
\r
4959 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4960 fromX = fromY = -1;
\r
4963 case EP_BlackFerz:
\r
4964 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4965 fromX = fromY = -1;
\r
4968 case EP_BlackWazir:
\r
4969 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4970 fromX = fromY = -1;
\r
4973 case EP_BlackAlfil:
\r
4974 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4975 fromX = fromY = -1;
\r
4978 case EP_BlackCannon:
\r
4979 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4980 fromX = fromY = -1;
\r
4983 case EP_BlackCardinal:
\r
4984 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4985 fromX = fromY = -1;
\r
4988 case EP_BlackMarshall:
\r
4989 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4990 fromX = fromY = -1;
\r
4993 case EP_BlackKing:
\r
4994 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4995 fromX = fromY = -1;
\r
4998 case EP_EmptySquare:
\r
4999 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5000 fromX = fromY = -1;
\r
5003 case EP_ClearBoard:
\r
5004 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5005 fromX = fromY = -1;
\r
5009 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5010 fromX = fromY = -1;
\r
5014 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5015 fromX = fromY = -1;
\r
5019 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5020 fromX = fromY = -1;
\r
5024 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5025 fromX = fromY = -1;
\r
5029 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5030 fromX = fromY = -1;
\r
5034 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5035 fromX = fromY = -1;
\r
5039 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5040 fromX = fromY = -1;
\r
5044 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5045 fromX = fromY = -1;
\r
5049 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5050 fromX = fromY = -1;
\r
5054 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5060 case CLOCK_TIMER_ID:
\r
5061 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5062 clockTimerEvent = 0;
\r
5063 DecrementClocks(); /* call into back end */
\r
5065 case LOAD_GAME_TIMER_ID:
\r
5066 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5067 loadGameTimerEvent = 0;
\r
5068 AutoPlayGameLoop(); /* call into back end */
\r
5070 case ANALYSIS_TIMER_ID:
\r
5071 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5072 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5073 AnalysisPeriodicEvent(0);
\r
5075 KillTimer(hwnd, analysisTimerEvent);
\r
5076 analysisTimerEvent = 0;
\r
5079 case DELAYED_TIMER_ID:
\r
5080 KillTimer(hwnd, delayedTimerEvent);
\r
5081 delayedTimerEvent = 0;
\r
5082 delayedTimerCallback();
\r
5087 case WM_USER_Input:
\r
5088 InputEvent(hwnd, message, wParam, lParam);
\r
5091 /* [AS] Also move "attached" child windows */
\r
5092 case WM_WINDOWPOSCHANGING:
\r
5094 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5095 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5097 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5098 /* Window is moving */
\r
5101 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5102 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5103 rcMain.right = wpMain.x + wpMain.width;
\r
5104 rcMain.top = wpMain.y;
\r
5105 rcMain.bottom = wpMain.y + wpMain.height;
\r
5107 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5108 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5109 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5110 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5111 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5112 wpMain.x = lpwp->x;
\r
5113 wpMain.y = lpwp->y;
\r
5118 /* [AS] Snapping */
\r
5119 case WM_ENTERSIZEMOVE:
\r
5120 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5121 if (hwnd == hwndMain) {
\r
5122 doingSizing = TRUE;
\r
5125 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5129 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5130 if (hwnd == hwndMain) {
\r
5131 lastSizing = wParam;
\r
5136 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5137 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5139 case WM_EXITSIZEMOVE:
\r
5140 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5141 if (hwnd == hwndMain) {
\r
5143 doingSizing = FALSE;
\r
5144 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5145 GetClientRect(hwnd, &client);
\r
5146 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5148 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5150 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5153 case WM_DESTROY: /* message: window being destroyed */
\r
5154 PostQuitMessage(0);
\r
5158 if (hwnd == hwndMain) {
\r
5163 default: /* Passes it on if unprocessed */
\r
5164 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5169 /*---------------------------------------------------------------------------*\
\r
5171 * Misc utility routines
\r
5173 \*---------------------------------------------------------------------------*/
\r
5176 * Decent random number generator, at least not as bad as Windows
\r
5177 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5179 unsigned int randstate;
\r
5184 randstate = randstate * 1664525 + 1013904223;
\r
5185 return (int) randstate & 0x7fffffff;
\r
5189 mysrandom(unsigned int seed)
\r
5196 * returns TRUE if user selects a different color, FALSE otherwise
\r
5200 ChangeColor(HWND hwnd, COLORREF *which)
\r
5202 static BOOL firstTime = TRUE;
\r
5203 static DWORD customColors[16];
\r
5205 COLORREF newcolor;
\r
5210 /* Make initial colors in use available as custom colors */
\r
5211 /* Should we put the compiled-in defaults here instead? */
\r
5213 customColors[i++] = lightSquareColor & 0xffffff;
\r
5214 customColors[i++] = darkSquareColor & 0xffffff;
\r
5215 customColors[i++] = whitePieceColor & 0xffffff;
\r
5216 customColors[i++] = blackPieceColor & 0xffffff;
\r
5217 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5218 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5220 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5221 customColors[i++] = textAttribs[ccl].color;
\r
5223 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5224 firstTime = FALSE;
\r
5227 cc.lStructSize = sizeof(cc);
\r
5228 cc.hwndOwner = hwnd;
\r
5229 cc.hInstance = NULL;
\r
5230 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5231 cc.lpCustColors = (LPDWORD) customColors;
\r
5232 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5234 if (!ChooseColor(&cc)) return FALSE;
\r
5236 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5237 if (newcolor == *which) return FALSE;
\r
5238 *which = newcolor;
\r
5242 InitDrawingColors();
\r
5243 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5248 MyLoadSound(MySound *ms)
\r
5254 if (ms->data) free(ms->data);
\r
5257 switch (ms->name[0]) {
\r
5263 /* System sound from Control Panel. Don't preload here. */
\r
5267 if (ms->name[1] == NULLCHAR) {
\r
5268 /* "!" alone = silence */
\r
5271 /* Builtin wave resource. Error if not found. */
\r
5272 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5273 if (h == NULL) break;
\r
5274 ms->data = (void *)LoadResource(hInst, h);
\r
5275 if (h == NULL) break;
\r
5280 /* .wav file. Error if not found. */
\r
5281 f = fopen(ms->name, "rb");
\r
5282 if (f == NULL) break;
\r
5283 if (fstat(fileno(f), &st) < 0) break;
\r
5284 ms->data = malloc(st.st_size);
\r
5285 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5291 char buf[MSG_SIZ];
\r
5292 sprintf(buf, "Error loading sound %s", ms->name);
\r
5293 DisplayError(buf, GetLastError());
\r
5299 MyPlaySound(MySound *ms)
\r
5301 BOOLEAN ok = FALSE;
\r
5303 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5304 switch (ms->name[0]) {
\r
5306 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5311 /* System sound from Control Panel (deprecated feature).
\r
5312 "$" alone or an unset sound name gets default beep (still in use). */
\r
5313 if (ms->name[1]) {
\r
5314 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5316 if (!ok) ok = MessageBeep(MB_OK);
\r
5319 /* Builtin wave resource, or "!" alone for silence */
\r
5320 if (ms->name[1]) {
\r
5321 if (ms->data == NULL) return FALSE;
\r
5322 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5328 /* .wav file. Error if not found. */
\r
5329 if (ms->data == NULL) return FALSE;
\r
5330 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5333 /* Don't print an error: this can happen innocently if the sound driver
\r
5334 is busy; for instance, if another instance of WinBoard is playing
\r
5335 a sound at about the same time. */
\r
5341 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5344 OPENFILENAME *ofn;
\r
5345 static UINT *number; /* gross that this is static */
\r
5347 switch (message) {
\r
5348 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5349 /* Center the dialog over the application window */
\r
5350 ofn = (OPENFILENAME *) lParam;
\r
5351 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5352 number = (UINT *) ofn->lCustData;
\r
5353 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5357 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5358 return FALSE; /* Allow for further processing */
\r
5361 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5362 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5364 return FALSE; /* Allow for further processing */
\r
5370 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5372 static UINT *number;
\r
5373 OPENFILENAME *ofname;
\r
5376 case WM_INITDIALOG:
\r
5377 ofname = (OPENFILENAME *)lParam;
\r
5378 number = (UINT *)(ofname->lCustData);
\r
5381 ofnot = (OFNOTIFY *)lParam;
\r
5382 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5383 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5392 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5393 char *nameFilt, char *dlgTitle, UINT *number,
\r
5394 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5396 OPENFILENAME openFileName;
\r
5397 char buf1[MSG_SIZ];
\r
5400 if (fileName == NULL) fileName = buf1;
\r
5401 if (defName == NULL) {
\r
5402 strcpy(fileName, "*.");
\r
5403 strcat(fileName, defExt);
\r
5405 strcpy(fileName, defName);
\r
5407 if (fileTitle) strcpy(fileTitle, "");
\r
5408 if (number) *number = 0;
\r
5410 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5411 openFileName.hwndOwner = hwnd;
\r
5412 openFileName.hInstance = (HANDLE) hInst;
\r
5413 openFileName.lpstrFilter = nameFilt;
\r
5414 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5415 openFileName.nMaxCustFilter = 0L;
\r
5416 openFileName.nFilterIndex = 1L;
\r
5417 openFileName.lpstrFile = fileName;
\r
5418 openFileName.nMaxFile = MSG_SIZ;
\r
5419 openFileName.lpstrFileTitle = fileTitle;
\r
5420 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5421 openFileName.lpstrInitialDir = NULL;
\r
5422 openFileName.lpstrTitle = dlgTitle;
\r
5423 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5424 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5425 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5426 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5427 openFileName.nFileOffset = 0;
\r
5428 openFileName.nFileExtension = 0;
\r
5429 openFileName.lpstrDefExt = defExt;
\r
5430 openFileName.lCustData = (LONG) number;
\r
5431 openFileName.lpfnHook = oldDialog ?
\r
5432 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5433 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5435 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5436 GetOpenFileName(&openFileName)) {
\r
5437 /* open the file */
\r
5438 f = fopen(openFileName.lpstrFile, write);
\r
5440 MessageBox(hwnd, "File open failed", NULL,
\r
5441 MB_OK|MB_ICONEXCLAMATION);
\r
5445 int err = CommDlgExtendedError();
\r
5446 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5455 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5457 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5460 * Get the first pop-up menu in the menu template. This is the
\r
5461 * menu that TrackPopupMenu displays.
\r
5463 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5465 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5468 * TrackPopup uses screen coordinates, so convert the
\r
5469 * coordinates of the mouse click to screen coordinates.
\r
5471 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5473 /* Draw and track the floating pop-up menu. */
\r
5474 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5475 pt.x, pt.y, 0, hwnd, NULL);
\r
5477 /* Destroy the menu.*/
\r
5478 DestroyMenu(hmenu);
\r
5483 int sizeX, sizeY, newSizeX, newSizeY;
\r
5485 } ResizeEditPlusButtonsClosure;
\r
5488 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5490 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5494 if (hChild == cl->hText) return TRUE;
\r
5495 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5496 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5497 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5498 ScreenToClient(cl->hDlg, &pt);
\r
5499 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5500 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5504 /* Resize a dialog that has a (rich) edit field filling most of
\r
5505 the top, with a row of buttons below */
\r
5507 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5510 int newTextHeight, newTextWidth;
\r
5511 ResizeEditPlusButtonsClosure cl;
\r
5513 /*if (IsIconic(hDlg)) return;*/
\r
5514 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5516 cl.hdwp = BeginDeferWindowPos(8);
\r
5518 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5519 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5520 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5521 if (newTextHeight < 0) {
\r
5522 newSizeY += -newTextHeight;
\r
5523 newTextHeight = 0;
\r
5525 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5526 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5532 cl.newSizeX = newSizeX;
\r
5533 cl.newSizeY = newSizeY;
\r
5534 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5536 EndDeferWindowPos(cl.hdwp);
\r
5539 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5541 RECT rChild, rParent;
\r
5542 int wChild, hChild, wParent, hParent;
\r
5543 int wScreen, hScreen, xNew, yNew;
\r
5546 /* Get the Height and Width of the child window */
\r
5547 GetWindowRect (hwndChild, &rChild);
\r
5548 wChild = rChild.right - rChild.left;
\r
5549 hChild = rChild.bottom - rChild.top;
\r
5551 /* Get the Height and Width of the parent window */
\r
5552 GetWindowRect (hwndParent, &rParent);
\r
5553 wParent = rParent.right - rParent.left;
\r
5554 hParent = rParent.bottom - rParent.top;
\r
5556 /* Get the display limits */
\r
5557 hdc = GetDC (hwndChild);
\r
5558 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5559 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5560 ReleaseDC(hwndChild, hdc);
\r
5562 /* Calculate new X position, then adjust for screen */
\r
5563 xNew = rParent.left + ((wParent - wChild) /2);
\r
5566 } else if ((xNew+wChild) > wScreen) {
\r
5567 xNew = wScreen - wChild;
\r
5570 /* Calculate new Y position, then adjust for screen */
\r
5572 yNew = rParent.top + ((hParent - hChild) /2);
\r
5575 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5580 } else if ((yNew+hChild) > hScreen) {
\r
5581 yNew = hScreen - hChild;
\r
5584 /* Set it, and return */
\r
5585 return SetWindowPos (hwndChild, NULL,
\r
5586 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5589 /* Center one window over another */
\r
5590 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5592 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5595 /*---------------------------------------------------------------------------*\
\r
5597 * Startup Dialog functions
\r
5599 \*---------------------------------------------------------------------------*/
\r
5601 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5603 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5605 while (*cd != NULL) {
\r
5606 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5612 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5614 char buf1[MAX_ARG_LEN];
\r
5617 if (str[0] == '@') {
\r
5618 FILE* f = fopen(str + 1, "r");
\r
5620 DisplayFatalError(str + 1, errno, 2);
\r
5623 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5625 buf1[len] = NULLCHAR;
\r
5629 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5632 char buf[MSG_SIZ];
\r
5633 char *end = strchr(str, '\n');
\r
5634 if (end == NULL) return;
\r
5635 memcpy(buf, str, end - str);
\r
5636 buf[end - str] = NULLCHAR;
\r
5637 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5643 SetStartupDialogEnables(HWND hDlg)
\r
5645 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5646 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5647 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5648 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5649 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5650 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5651 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5652 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5653 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5654 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5655 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5656 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5657 IsDlgButtonChecked(hDlg, OPT_View));
\r
5661 QuoteForFilename(char *filename)
\r
5663 int dquote, space;
\r
5664 dquote = strchr(filename, '"') != NULL;
\r
5665 space = strchr(filename, ' ') != NULL;
\r
5666 if (dquote || space) {
\r
5678 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5680 char buf[MSG_SIZ];
\r
5683 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5684 q = QuoteForFilename(nthcp);
\r
5685 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5686 if (*nthdir != NULLCHAR) {
\r
5687 q = QuoteForFilename(nthdir);
\r
5688 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5690 if (*nthcp == NULLCHAR) {
\r
5691 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5692 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5693 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5694 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5699 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5701 char buf[MSG_SIZ];
\r
5705 switch (message) {
\r
5706 case WM_INITDIALOG:
\r
5707 /* Center the dialog */
\r
5708 CenterWindow (hDlg, GetDesktopWindow());
\r
5709 /* Initialize the dialog items */
\r
5710 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5711 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5712 firstChessProgramNames);
\r
5713 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5714 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5715 secondChessProgramNames);
\r
5716 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5717 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5718 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5719 if (*appData.icsHelper != NULLCHAR) {
\r
5720 char *q = QuoteForFilename(appData.icsHelper);
\r
5721 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5723 if (*appData.icsHost == NULLCHAR) {
\r
5724 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5725 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5726 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5727 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5728 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5731 if (appData.icsActive) {
\r
5732 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5734 else if (appData.noChessProgram) {
\r
5735 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5738 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5741 SetStartupDialogEnables(hDlg);
\r
5745 switch (LOWORD(wParam)) {
\r
5747 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5748 strcpy(buf, "/fcp=");
\r
5749 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5751 ParseArgs(StringGet, &p);
\r
5752 strcpy(buf, "/scp=");
\r
5753 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5755 ParseArgs(StringGet, &p);
\r
5756 appData.noChessProgram = FALSE;
\r
5757 appData.icsActive = FALSE;
\r
5758 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5759 strcpy(buf, "/ics /icshost=");
\r
5760 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5762 ParseArgs(StringGet, &p);
\r
5763 if (appData.zippyPlay) {
\r
5764 strcpy(buf, "/fcp=");
\r
5765 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5767 ParseArgs(StringGet, &p);
\r
5769 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5770 appData.noChessProgram = TRUE;
\r
5771 appData.icsActive = FALSE;
\r
5773 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5774 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5777 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5778 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5780 ParseArgs(StringGet, &p);
\r
5782 EndDialog(hDlg, TRUE);
\r
5789 case IDM_HELPCONTENTS:
\r
5790 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5791 MessageBox (GetFocus(),
\r
5792 "Unable to activate help",
\r
5793 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5798 SetStartupDialogEnables(hDlg);
\r
5806 /*---------------------------------------------------------------------------*\
\r
5808 * About box dialog functions
\r
5810 \*---------------------------------------------------------------------------*/
\r
5812 /* Process messages for "About" dialog box */
\r
5814 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5816 switch (message) {
\r
5817 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5818 /* Center the dialog over the application window */
\r
5819 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5820 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5824 case WM_COMMAND: /* message: received a command */
\r
5825 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5826 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5827 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5835 /*---------------------------------------------------------------------------*\
\r
5837 * Comment Dialog functions
\r
5839 \*---------------------------------------------------------------------------*/
\r
5842 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5844 static HANDLE hwndText = NULL;
\r
5845 int len, newSizeX, newSizeY, flags;
\r
5846 static int sizeX, sizeY;
\r
5851 switch (message) {
\r
5852 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5853 /* Initialize the dialog items */
\r
5854 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5855 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5856 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5857 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5858 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5859 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5860 SetWindowText(hDlg, commentTitle);
\r
5861 if (editComment) {
\r
5862 SetFocus(hwndText);
\r
5864 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5866 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5867 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5868 MAKELPARAM(FALSE, 0));
\r
5869 /* Size and position the dialog */
\r
5870 if (!commentDialog) {
\r
5871 commentDialog = hDlg;
\r
5872 flags = SWP_NOZORDER;
\r
5873 GetClientRect(hDlg, &rect);
\r
5874 sizeX = rect.right;
\r
5875 sizeY = rect.bottom;
\r
5876 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5877 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5878 WINDOWPLACEMENT wp;
\r
5879 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5880 wp.length = sizeof(WINDOWPLACEMENT);
\r
5882 wp.showCmd = SW_SHOW;
\r
5883 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5884 wp.rcNormalPosition.left = wpComment.x;
\r
5885 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5886 wp.rcNormalPosition.top = wpComment.y;
\r
5887 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5888 SetWindowPlacement(hDlg, &wp);
\r
5890 GetClientRect(hDlg, &rect);
\r
5891 newSizeX = rect.right;
\r
5892 newSizeY = rect.bottom;
\r
5893 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5894 newSizeX, newSizeY);
\r
5899 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5902 case WM_COMMAND: /* message: received a command */
\r
5903 switch (LOWORD(wParam)) {
\r
5905 if (editComment) {
\r
5907 /* Read changed options from the dialog box */
\r
5908 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5909 len = GetWindowTextLength(hwndText);
\r
5910 str = (char *) malloc(len + 1);
\r
5911 GetWindowText(hwndText, str, len + 1);
\r
5920 ReplaceComment(commentIndex, str);
\r
5927 case OPT_CancelComment:
\r
5931 case OPT_ClearComment:
\r
5932 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5935 case OPT_EditComment:
\r
5936 EditCommentEvent();
\r
5944 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5945 if( wParam == OPT_CommentText ) {
\r
5946 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5948 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5952 pt.x = LOWORD( lpMF->lParam );
\r
5953 pt.y = HIWORD( lpMF->lParam );
\r
5955 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5957 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5958 len = GetWindowTextLength(hwndText);
\r
5959 str = (char *) malloc(len + 1);
\r
5960 GetWindowText(hwndText, str, len + 1);
\r
5961 ReplaceComment(commentIndex, str);
\r
5962 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5963 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5966 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5967 lpMF->msg = WM_USER;
\r
5975 newSizeX = LOWORD(lParam);
\r
5976 newSizeY = HIWORD(lParam);
\r
5977 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5982 case WM_GETMINMAXINFO:
\r
5983 /* Prevent resizing window too small */
\r
5984 mmi = (MINMAXINFO *) lParam;
\r
5985 mmi->ptMinTrackSize.x = 100;
\r
5986 mmi->ptMinTrackSize.y = 100;
\r
5993 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5998 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6000 if (str == NULL) str = "";
\r
6001 p = (char *) malloc(2 * strlen(str) + 2);
\r
6004 if (*str == '\n') *q++ = '\r';
\r
6008 if (commentText != NULL) free(commentText);
\r
6010 commentIndex = index;
\r
6011 commentTitle = title;
\r
6013 editComment = edit;
\r
6015 if (commentDialog) {
\r
6016 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6017 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6019 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6020 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6021 hwndMain, (DLGPROC)lpProc);
\r
6022 FreeProcInstance(lpProc);
\r
6028 /*---------------------------------------------------------------------------*\
\r
6030 * Type-in move dialog functions
\r
6032 \*---------------------------------------------------------------------------*/
\r
6035 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6037 char move[MSG_SIZ];
\r
6039 ChessMove moveType;
\r
6040 int fromX, fromY, toX, toY;
\r
6043 switch (message) {
\r
6044 case WM_INITDIALOG:
\r
6045 move[0] = (char) lParam;
\r
6046 move[1] = NULLCHAR;
\r
6047 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6048 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6049 SetWindowText(hInput, move);
\r
6051 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6055 switch (LOWORD(wParam)) {
\r
6057 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6058 { int n; Board board;
\r
6060 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6061 EditPositionPasteFEN(move);
\r
6062 EndDialog(hDlg, TRUE);
\r
6065 // [HGM] movenum: allow move number to be typed in any mode
\r
6066 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6068 EndDialog(hDlg, TRUE);
\r
6072 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6073 gameMode != Training) {
\r
6074 DisplayMoveError("Displayed move is not current");
\r
6076 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6077 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6078 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6079 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6080 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6081 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6082 if (gameMode != Training)
\r
6083 forwardMostMove = currentMove;
\r
6084 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6086 DisplayMoveError("Could not parse move");
\r
6089 EndDialog(hDlg, TRUE);
\r
6092 EndDialog(hDlg, FALSE);
\r
6103 PopUpMoveDialog(char firstchar)
\r
6107 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6108 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6109 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6110 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6111 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6112 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6113 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6114 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6115 gameMode == Training) {
\r
6116 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6117 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6118 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6119 FreeProcInstance(lpProc);
\r
6123 /*---------------------------------------------------------------------------*\
\r
6125 * Type-in name dialog functions
\r
6127 \*---------------------------------------------------------------------------*/
\r
6130 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6132 char move[MSG_SIZ];
\r
6135 switch (message) {
\r
6136 case WM_INITDIALOG:
\r
6137 move[0] = (char) lParam;
\r
6138 move[1] = NULLCHAR;
\r
6139 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6140 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6141 SetWindowText(hInput, move);
\r
6143 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6147 switch (LOWORD(wParam)) {
\r
6149 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6150 appData.userName = strdup(move);
\r
6153 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6154 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6155 DisplayTitle(move);
\r
6159 EndDialog(hDlg, TRUE);
\r
6162 EndDialog(hDlg, FALSE);
\r
6173 PopUpNameDialog(char firstchar)
\r
6177 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6178 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6179 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6180 FreeProcInstance(lpProc);
\r
6183 /*---------------------------------------------------------------------------*\
\r
6187 \*---------------------------------------------------------------------------*/
\r
6189 /* Nonmodal error box */
\r
6190 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6191 WPARAM wParam, LPARAM lParam);
\r
6194 ErrorPopUp(char *title, char *content)
\r
6198 BOOLEAN modal = hwndMain == NULL;
\r
6216 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6217 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6220 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6222 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6223 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6224 hwndMain, (DLGPROC)lpProc);
\r
6225 FreeProcInstance(lpProc);
\r
6232 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6233 if (errorDialog == NULL) return;
\r
6234 DestroyWindow(errorDialog);
\r
6235 errorDialog = NULL;
\r
6236 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6240 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6245 switch (message) {
\r
6246 case WM_INITDIALOG:
\r
6247 GetWindowRect(hDlg, &rChild);
\r
6250 SetWindowPos(hDlg, NULL, rChild.left,
\r
6251 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6252 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6256 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6257 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6258 and it doesn't work when you resize the dialog.
\r
6259 For now, just give it a default position.
\r
6261 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6263 errorDialog = hDlg;
\r
6264 SetWindowText(hDlg, errorTitle);
\r
6265 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6266 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6270 switch (LOWORD(wParam)) {
\r
6273 if (errorDialog == hDlg) errorDialog = NULL;
\r
6274 DestroyWindow(hDlg);
\r
6286 HWND gothicDialog = NULL;
\r
6289 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6293 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6295 switch (message) {
\r
6296 case WM_INITDIALOG:
\r
6297 GetWindowRect(hDlg, &rChild);
\r
6299 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6303 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6304 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6305 and it doesn't work when you resize the dialog.
\r
6306 For now, just give it a default position.
\r
6308 gothicDialog = hDlg;
\r
6309 SetWindowText(hDlg, errorTitle);
\r
6310 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6311 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6315 switch (LOWORD(wParam)) {
\r
6318 if (errorDialog == hDlg) errorDialog = NULL;
\r
6319 DestroyWindow(hDlg);
\r
6331 GothicPopUp(char *title, VariantClass variant)
\r
6334 static char *lastTitle;
\r
6336 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6337 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6339 if(lastTitle != title && gothicDialog != NULL) {
\r
6340 DestroyWindow(gothicDialog);
\r
6341 gothicDialog = NULL;
\r
6343 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6344 title = lastTitle;
\r
6345 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6346 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6347 hwndMain, (DLGPROC)lpProc);
\r
6348 FreeProcInstance(lpProc);
\r
6353 /*---------------------------------------------------------------------------*\
\r
6355 * Ics Interaction console functions
\r
6357 \*---------------------------------------------------------------------------*/
\r
6359 #define HISTORY_SIZE 64
\r
6360 static char *history[HISTORY_SIZE];
\r
6361 int histIn = 0, histP = 0;
\r
6364 SaveInHistory(char *cmd)
\r
6366 if (history[histIn] != NULL) {
\r
6367 free(history[histIn]);
\r
6368 history[histIn] = NULL;
\r
6370 if (*cmd == NULLCHAR) return;
\r
6371 history[histIn] = StrSave(cmd);
\r
6372 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6373 if (history[histIn] != NULL) {
\r
6374 free(history[histIn]);
\r
6375 history[histIn] = NULL;
\r
6381 PrevInHistory(char *cmd)
\r
6384 if (histP == histIn) {
\r
6385 if (history[histIn] != NULL) free(history[histIn]);
\r
6386 history[histIn] = StrSave(cmd);
\r
6388 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6389 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6391 return history[histP];
\r
6397 if (histP == histIn) return NULL;
\r
6398 histP = (histP + 1) % HISTORY_SIZE;
\r
6399 return history[histP];
\r
6403 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6407 hmenu = LoadMenu(hInst, "TextMenu");
\r
6408 h = GetSubMenu(hmenu, 0);
\r
6410 if (strcmp(e->item, "-") == 0) {
\r
6411 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6412 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6413 int flags = MF_STRING, j = 0;
\r
6414 if (e->item[0] == '|') {
\r
6415 flags |= MF_MENUBARBREAK;
\r
6418 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6419 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6427 WNDPROC consoleTextWindowProc;
\r
6430 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6432 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6433 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6437 SetWindowText(hInput, command);
\r
6439 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6441 sel.cpMin = 999999;
\r
6442 sel.cpMax = 999999;
\r
6443 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6448 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6449 if (sel.cpMin == sel.cpMax) {
\r
6450 /* Expand to surrounding word */
\r
6453 tr.chrg.cpMax = sel.cpMin;
\r
6454 tr.chrg.cpMin = --sel.cpMin;
\r
6455 if (sel.cpMin < 0) break;
\r
6456 tr.lpstrText = name;
\r
6457 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6458 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6462 tr.chrg.cpMin = sel.cpMax;
\r
6463 tr.chrg.cpMax = ++sel.cpMax;
\r
6464 tr.lpstrText = name;
\r
6465 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6466 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6469 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6470 MessageBeep(MB_ICONEXCLAMATION);
\r
6474 tr.lpstrText = name;
\r
6475 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6477 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6478 MessageBeep(MB_ICONEXCLAMATION);
\r
6481 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6484 sprintf(buf, "%s %s", command, name);
\r
6485 SetWindowText(hInput, buf);
\r
6486 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6488 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6489 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6490 SetWindowText(hInput, buf);
\r
6491 sel.cpMin = 999999;
\r
6492 sel.cpMax = 999999;
\r
6493 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6499 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6504 switch (message) {
\r
6506 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6509 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6512 sel.cpMin = 999999;
\r
6513 sel.cpMax = 999999;
\r
6514 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6515 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6520 if(wParam != '\022') {
\r
6521 if (wParam == '\t') {
\r
6522 if (GetKeyState(VK_SHIFT) < 0) {
\r
6524 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6525 if (buttonDesc[0].hwnd) {
\r
6526 SetFocus(buttonDesc[0].hwnd);
\r
6528 SetFocus(hwndMain);
\r
6532 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6535 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6536 JAWS_DELETE( SetFocus(hInput); )
\r
6537 SendMessage(hInput, message, wParam, lParam);
\r
6540 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6541 case WM_RBUTTONDOWN:
\r
6542 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6543 /* Move selection here if it was empty */
\r
6545 pt.x = LOWORD(lParam);
\r
6546 pt.y = HIWORD(lParam);
\r
6547 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6548 if (sel.cpMin == sel.cpMax) {
\r
6549 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6550 sel.cpMax = sel.cpMin;
\r
6551 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6553 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6554 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6556 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6557 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6558 if (sel.cpMin == sel.cpMax) {
\r
6559 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6560 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6562 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6563 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6565 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6566 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6567 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6568 MenuPopup(hwnd, pt, hmenu, -1);
\r
6572 case WM_RBUTTONUP:
\r
6573 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6574 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6575 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6579 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6581 return SendMessage(hInput, message, wParam, lParam);
\r
6582 case WM_MBUTTONDOWN:
\r
6583 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6585 switch (LOWORD(wParam)) {
\r
6586 case IDM_QuickPaste:
\r
6588 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6589 if (sel.cpMin == sel.cpMax) {
\r
6590 MessageBeep(MB_ICONEXCLAMATION);
\r
6593 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6594 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6595 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6600 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6603 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6606 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6610 int i = LOWORD(wParam) - IDM_CommandX;
\r
6611 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6612 icsTextMenuEntry[i].command != NULL) {
\r
6613 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6614 icsTextMenuEntry[i].getname,
\r
6615 icsTextMenuEntry[i].immediate);
\r
6623 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6626 WNDPROC consoleInputWindowProc;
\r
6629 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6631 char buf[MSG_SIZ];
\r
6633 static BOOL sendNextChar = FALSE;
\r
6634 static BOOL quoteNextChar = FALSE;
\r
6635 InputSource *is = consoleInputSource;
\r
6639 switch (message) {
\r
6641 if (!appData.localLineEditing || sendNextChar) {
\r
6642 is->buf[0] = (CHAR) wParam;
\r
6644 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6645 sendNextChar = FALSE;
\r
6648 if (quoteNextChar) {
\r
6649 buf[0] = (char) wParam;
\r
6650 buf[1] = NULLCHAR;
\r
6651 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6652 quoteNextChar = FALSE;
\r
6656 case '\r': /* Enter key */
\r
6657 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6658 if (consoleEcho) SaveInHistory(is->buf);
\r
6659 is->buf[is->count++] = '\n';
\r
6660 is->buf[is->count] = NULLCHAR;
\r
6661 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6662 if (consoleEcho) {
\r
6663 ConsoleOutput(is->buf, is->count, TRUE);
\r
6664 } else if (appData.localLineEditing) {
\r
6665 ConsoleOutput("\n", 1, TRUE);
\r
6668 case '\033': /* Escape key */
\r
6669 SetWindowText(hwnd, "");
\r
6670 cf.cbSize = sizeof(CHARFORMAT);
\r
6671 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6672 if (consoleEcho) {
\r
6673 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6675 cf.crTextColor = COLOR_ECHOOFF;
\r
6677 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6678 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6680 case '\t': /* Tab key */
\r
6681 if (GetKeyState(VK_SHIFT) < 0) {
\r
6683 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6686 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6687 if (buttonDesc[0].hwnd) {
\r
6688 SetFocus(buttonDesc[0].hwnd);
\r
6690 SetFocus(hwndMain);
\r
6694 case '\023': /* Ctrl+S */
\r
6695 sendNextChar = TRUE;
\r
6697 case '\021': /* Ctrl+Q */
\r
6698 quoteNextChar = TRUE;
\r
6708 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6709 p = PrevInHistory(buf);
\r
6711 SetWindowText(hwnd, p);
\r
6712 sel.cpMin = 999999;
\r
6713 sel.cpMax = 999999;
\r
6714 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6719 p = NextInHistory();
\r
6721 SetWindowText(hwnd, p);
\r
6722 sel.cpMin = 999999;
\r
6723 sel.cpMax = 999999;
\r
6724 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6730 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6734 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6738 case WM_MBUTTONDOWN:
\r
6739 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6740 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6742 case WM_RBUTTONUP:
\r
6743 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6744 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6745 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6749 hmenu = LoadMenu(hInst, "InputMenu");
\r
6750 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6751 if (sel.cpMin == sel.cpMax) {
\r
6752 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6753 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6755 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6756 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6758 pt.x = LOWORD(lParam);
\r
6759 pt.y = HIWORD(lParam);
\r
6760 MenuPopup(hwnd, pt, hmenu, -1);
\r
6764 switch (LOWORD(wParam)) {
\r
6766 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6768 case IDM_SelectAll:
\r
6770 sel.cpMax = -1; /*999999?*/
\r
6771 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6774 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6777 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6780 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6785 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6788 #define CO_MAX 100000
\r
6789 #define CO_TRIM 1000
\r
6792 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6794 static SnapData sd;
\r
6795 HWND hText, hInput;
\r
6797 static int sizeX, sizeY;
\r
6798 int newSizeX, newSizeY;
\r
6802 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6803 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6805 switch (message) {
\r
6807 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6809 ENLINK *pLink = (ENLINK*)lParam;
\r
6810 if (pLink->msg == WM_LBUTTONUP)
\r
6814 tr.chrg = pLink->chrg;
\r
6815 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6816 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6817 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6818 free(tr.lpstrText);
\r
6822 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6823 hwndConsole = hDlg;
\r
6825 consoleTextWindowProc = (WNDPROC)
\r
6826 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6827 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6828 consoleInputWindowProc = (WNDPROC)
\r
6829 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6830 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6831 Colorize(ColorNormal, TRUE);
\r
6832 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6833 ChangedConsoleFont();
\r
6834 GetClientRect(hDlg, &rect);
\r
6835 sizeX = rect.right;
\r
6836 sizeY = rect.bottom;
\r
6837 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6838 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6839 WINDOWPLACEMENT wp;
\r
6840 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6841 wp.length = sizeof(WINDOWPLACEMENT);
\r
6843 wp.showCmd = SW_SHOW;
\r
6844 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6845 wp.rcNormalPosition.left = wpConsole.x;
\r
6846 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6847 wp.rcNormalPosition.top = wpConsole.y;
\r
6848 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6849 SetWindowPlacement(hDlg, &wp);
\r
6852 // [HGM] Chessknight's change 2004-07-13
\r
6853 else { /* Determine Defaults */
\r
6854 WINDOWPLACEMENT wp;
\r
6855 wpConsole.x = wpMain.width + 1;
\r
6856 wpConsole.y = wpMain.y;
\r
6857 wpConsole.width = screenWidth - wpMain.width;
\r
6858 wpConsole.height = wpMain.height;
\r
6859 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6860 wp.length = sizeof(WINDOWPLACEMENT);
\r
6862 wp.showCmd = SW_SHOW;
\r
6863 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6864 wp.rcNormalPosition.left = wpConsole.x;
\r
6865 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6866 wp.rcNormalPosition.top = wpConsole.y;
\r
6867 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6868 SetWindowPlacement(hDlg, &wp);
\r
6871 // Allow hText to highlight URLs and send notifications on them
\r
6872 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6873 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6874 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6875 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6889 if (IsIconic(hDlg)) break;
\r
6890 newSizeX = LOWORD(lParam);
\r
6891 newSizeY = HIWORD(lParam);
\r
6892 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6893 RECT rectText, rectInput;
\r
6895 int newTextHeight, newTextWidth;
\r
6896 GetWindowRect(hText, &rectText);
\r
6897 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6898 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6899 if (newTextHeight < 0) {
\r
6900 newSizeY += -newTextHeight;
\r
6901 newTextHeight = 0;
\r
6903 SetWindowPos(hText, NULL, 0, 0,
\r
6904 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6905 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6906 pt.x = rectInput.left;
\r
6907 pt.y = rectInput.top + newSizeY - sizeY;
\r
6908 ScreenToClient(hDlg, &pt);
\r
6909 SetWindowPos(hInput, NULL,
\r
6910 pt.x, pt.y, /* needs client coords */
\r
6911 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6912 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6918 case WM_GETMINMAXINFO:
\r
6919 /* Prevent resizing window too small */
\r
6920 mmi = (MINMAXINFO *) lParam;
\r
6921 mmi->ptMinTrackSize.x = 100;
\r
6922 mmi->ptMinTrackSize.y = 100;
\r
6925 /* [AS] Snapping */
\r
6926 case WM_ENTERSIZEMOVE:
\r
6927 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6930 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6933 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6935 case WM_EXITSIZEMOVE:
\r
6936 UpdateICSWidth(hText);
\r
6937 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6940 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6948 if (hwndConsole) return;
\r
6949 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6950 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6955 ConsoleOutput(char* data, int length, int forceVisible)
\r
6960 char buf[CO_MAX+1];
\r
6963 static int delayLF = 0;
\r
6964 CHARRANGE savesel, sel;
\r
6966 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6974 while (length--) {
\r
6982 } else if (*p == '\007') {
\r
6983 MyPlaySound(&sounds[(int)SoundBell]);
\r
6990 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6991 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6992 /* Save current selection */
\r
6993 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6994 exlen = GetWindowTextLength(hText);
\r
6995 /* Find out whether current end of text is visible */
\r
6996 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6997 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6998 /* Trim existing text if it's too long */
\r
6999 if (exlen + (q - buf) > CO_MAX) {
\r
7000 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7003 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7004 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7006 savesel.cpMin -= trim;
\r
7007 savesel.cpMax -= trim;
\r
7008 if (exlen < 0) exlen = 0;
\r
7009 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7010 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7012 /* Append the new text */
\r
7013 sel.cpMin = exlen;
\r
7014 sel.cpMax = exlen;
\r
7015 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7016 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7017 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7018 if (forceVisible || exlen == 0 ||
\r
7019 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7020 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7021 /* Scroll to make new end of text visible if old end of text
\r
7022 was visible or new text is an echo of user typein */
\r
7023 sel.cpMin = 9999999;
\r
7024 sel.cpMax = 9999999;
\r
7025 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7026 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7027 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7028 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7030 if (savesel.cpMax == exlen || forceVisible) {
\r
7031 /* Move insert point to new end of text if it was at the old
\r
7032 end of text or if the new text is an echo of user typein */
\r
7033 sel.cpMin = 9999999;
\r
7034 sel.cpMax = 9999999;
\r
7035 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7037 /* Restore previous selection */
\r
7038 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7040 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7047 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7051 COLORREF oldFg, oldBg;
\r
7055 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7057 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7058 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7059 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7062 rect.right = x + squareSize;
\r
7064 rect.bottom = y + squareSize;
\r
7067 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7068 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7069 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7070 &rect, str, strlen(str), NULL);
\r
7072 (void) SetTextColor(hdc, oldFg);
\r
7073 (void) SetBkColor(hdc, oldBg);
\r
7074 (void) SelectObject(hdc, oldFont);
\r
7078 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7079 RECT *rect, char *color, char *flagFell)
\r
7083 COLORREF oldFg, oldBg;
\r
7086 if (appData.clockMode) {
\r
7088 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7090 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7097 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7098 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7100 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7101 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7103 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7107 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7108 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7109 rect, str, strlen(str), NULL);
\r
7110 if(logoHeight > 0 && appData.clockMode) {
\r
7112 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7113 r.top = rect->top + logoHeight/2;
\r
7114 r.left = rect->left;
\r
7115 r.right = rect->right;
\r
7116 r.bottom = rect->bottom;
\r
7117 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7118 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7119 &r, str, strlen(str), NULL);
\r
7121 (void) SetTextColor(hdc, oldFg);
\r
7122 (void) SetBkColor(hdc, oldBg);
\r
7123 (void) SelectObject(hdc, oldFont);
\r
7128 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7134 if( count <= 0 ) {
\r
7135 if (appData.debugMode) {
\r
7136 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7139 return ERROR_INVALID_USER_BUFFER;
\r
7142 ResetEvent(ovl->hEvent);
\r
7143 ovl->Offset = ovl->OffsetHigh = 0;
\r
7144 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7148 err = GetLastError();
\r
7149 if (err == ERROR_IO_PENDING) {
\r
7150 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7154 err = GetLastError();
\r
7161 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7166 ResetEvent(ovl->hEvent);
\r
7167 ovl->Offset = ovl->OffsetHigh = 0;
\r
7168 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7172 err = GetLastError();
\r
7173 if (err == ERROR_IO_PENDING) {
\r
7174 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7178 err = GetLastError();
\r
7184 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7185 void CheckForInputBufferFull( InputSource * is )
\r
7187 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7188 /* Look for end of line */
\r
7189 char * p = is->buf;
\r
7191 while( p < is->next && *p != '\n' ) {
\r
7195 if( p >= is->next ) {
\r
7196 if (appData.debugMode) {
\r
7197 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7200 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7201 is->count = (DWORD) -1;
\r
7202 is->next = is->buf;
\r
7208 InputThread(LPVOID arg)
\r
7213 is = (InputSource *) arg;
\r
7214 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7215 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7216 while (is->hThread != NULL) {
\r
7217 is->error = DoReadFile(is->hFile, is->next,
\r
7218 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7219 &is->count, &ovl);
\r
7220 if (is->error == NO_ERROR) {
\r
7221 is->next += is->count;
\r
7223 if (is->error == ERROR_BROKEN_PIPE) {
\r
7224 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7227 is->count = (DWORD) -1;
\r
7228 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7233 CheckForInputBufferFull( is );
\r
7235 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7237 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7239 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7242 CloseHandle(ovl.hEvent);
\r
7243 CloseHandle(is->hFile);
\r
7245 if (appData.debugMode) {
\r
7246 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7253 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7255 NonOvlInputThread(LPVOID arg)
\r
7262 is = (InputSource *) arg;
\r
7263 while (is->hThread != NULL) {
\r
7264 is->error = ReadFile(is->hFile, is->next,
\r
7265 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7266 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7267 if (is->error == NO_ERROR) {
\r
7268 /* Change CRLF to LF */
\r
7269 if (is->next > is->buf) {
\r
7271 i = is->count + 1;
\r
7279 if (prev == '\r' && *p == '\n') {
\r
7291 if (is->error == ERROR_BROKEN_PIPE) {
\r
7292 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7295 is->count = (DWORD) -1;
\r
7299 CheckForInputBufferFull( is );
\r
7301 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7303 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7305 if (is->count < 0) break; /* Quit on error */
\r
7307 CloseHandle(is->hFile);
\r
7312 SocketInputThread(LPVOID arg)
\r
7316 is = (InputSource *) arg;
\r
7317 while (is->hThread != NULL) {
\r
7318 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7319 if ((int)is->count == SOCKET_ERROR) {
\r
7320 is->count = (DWORD) -1;
\r
7321 is->error = WSAGetLastError();
\r
7323 is->error = NO_ERROR;
\r
7324 is->next += is->count;
\r
7325 if (is->count == 0 && is->second == is) {
\r
7326 /* End of file on stderr; quit with no message */
\r
7330 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7332 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7334 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7340 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7344 is = (InputSource *) lParam;
\r
7345 if (is->lineByLine) {
\r
7346 /* Feed in lines one by one */
\r
7347 char *p = is->buf;
\r
7349 while (q < is->next) {
\r
7350 if (*q++ == '\n') {
\r
7351 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7356 /* Move any partial line to the start of the buffer */
\r
7358 while (p < is->next) {
\r
7363 if (is->error != NO_ERROR || is->count == 0) {
\r
7364 /* Notify backend of the error. Note: If there was a partial
\r
7365 line at the end, it is not flushed through. */
\r
7366 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7369 /* Feed in the whole chunk of input at once */
\r
7370 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7371 is->next = is->buf;
\r
7375 /*---------------------------------------------------------------------------*\
\r
7377 * Menu enables. Used when setting various modes.
\r
7379 \*---------------------------------------------------------------------------*/
\r
7387 GreyRevert(Boolean grey)
\r
7388 { // [HGM] vari: for retracting variations in local mode
\r
7389 HMENU hmenu = GetMenu(hwndMain);
\r
7390 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7391 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7395 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7397 while (enab->item > 0) {
\r
7398 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7403 Enables gnuEnables[] = {
\r
7404 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7405 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7406 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7407 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7408 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7409 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7410 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7411 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7412 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7413 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7415 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7416 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7420 Enables icsEnables[] = {
\r
7421 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7422 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7424 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7425 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7426 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7427 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7428 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7429 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7434 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7441 Enables zippyEnables[] = {
\r
7442 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7443 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7444 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7445 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7450 Enables ncpEnables[] = {
\r
7451 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7452 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7453 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7454 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7455 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7456 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7457 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7458 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7459 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7460 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7461 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7463 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7464 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7465 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7466 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7467 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7474 Enables trainingOnEnables[] = {
\r
7475 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7476 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7477 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7479 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7480 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7481 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7482 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7486 Enables trainingOffEnables[] = {
\r
7487 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7488 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7489 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7490 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7491 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7492 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7493 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7494 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7498 /* These modify either ncpEnables or gnuEnables */
\r
7499 Enables cmailEnables[] = {
\r
7500 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7501 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7502 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7503 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7504 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7505 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7506 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7510 Enables machineThinkingEnables[] = {
\r
7511 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7512 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7513 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7514 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7515 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7516 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7517 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7518 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7519 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7520 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7521 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7522 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7523 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7524 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7525 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7529 Enables userThinkingEnables[] = {
\r
7530 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7531 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7532 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7533 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7534 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7535 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7536 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7537 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7538 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7539 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7540 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7541 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7542 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7543 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7544 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7548 /*---------------------------------------------------------------------------*\
\r
7550 * Front-end interface functions exported by XBoard.
\r
7551 * Functions appear in same order as prototypes in frontend.h.
\r
7553 \*---------------------------------------------------------------------------*/
\r
7557 static UINT prevChecked = 0;
\r
7558 static int prevPausing = 0;
\r
7561 if (pausing != prevPausing) {
\r
7562 prevPausing = pausing;
\r
7563 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7564 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7565 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7568 switch (gameMode) {
\r
7569 case BeginningOfGame:
\r
7570 if (appData.icsActive)
\r
7571 nowChecked = IDM_IcsClient;
\r
7572 else if (appData.noChessProgram)
\r
7573 nowChecked = IDM_EditGame;
\r
7575 nowChecked = IDM_MachineBlack;
\r
7577 case MachinePlaysBlack:
\r
7578 nowChecked = IDM_MachineBlack;
\r
7580 case MachinePlaysWhite:
\r
7581 nowChecked = IDM_MachineWhite;
\r
7583 case TwoMachinesPlay:
\r
7584 nowChecked = IDM_TwoMachines;
\r
7587 nowChecked = IDM_AnalysisMode;
\r
7590 nowChecked = IDM_AnalyzeFile;
\r
7593 nowChecked = IDM_EditGame;
\r
7595 case PlayFromGameFile:
\r
7596 nowChecked = IDM_LoadGame;
\r
7598 case EditPosition:
\r
7599 nowChecked = IDM_EditPosition;
\r
7602 nowChecked = IDM_Training;
\r
7604 case IcsPlayingWhite:
\r
7605 case IcsPlayingBlack:
\r
7606 case IcsObserving:
\r
7608 nowChecked = IDM_IcsClient;
\r
7615 if (prevChecked != 0)
\r
7616 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7617 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7618 if (nowChecked != 0)
\r
7619 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7620 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7622 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7623 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7624 MF_BYCOMMAND|MF_ENABLED);
\r
7626 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7627 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7630 prevChecked = nowChecked;
\r
7632 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7633 if (appData.icsActive) {
\r
7634 if (appData.icsEngineAnalyze) {
\r
7635 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7636 MF_BYCOMMAND|MF_CHECKED);
\r
7638 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7639 MF_BYCOMMAND|MF_UNCHECKED);
\r
7647 HMENU hmenu = GetMenu(hwndMain);
\r
7648 SetMenuEnables(hmenu, icsEnables);
\r
7649 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7650 MF_BYPOSITION|MF_ENABLED);
\r
7652 if (appData.zippyPlay) {
\r
7653 SetMenuEnables(hmenu, zippyEnables);
\r
7654 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7655 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7656 MF_BYCOMMAND|MF_ENABLED);
\r
7664 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7670 HMENU hmenu = GetMenu(hwndMain);
\r
7671 SetMenuEnables(hmenu, ncpEnables);
\r
7672 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7673 MF_BYPOSITION|MF_GRAYED);
\r
7674 DrawMenuBar(hwndMain);
\r
7680 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7684 SetTrainingModeOn()
\r
7687 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7688 for (i = 0; i < N_BUTTONS; i++) {
\r
7689 if (buttonDesc[i].hwnd != NULL)
\r
7690 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7695 VOID SetTrainingModeOff()
\r
7698 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7699 for (i = 0; i < N_BUTTONS; i++) {
\r
7700 if (buttonDesc[i].hwnd != NULL)
\r
7701 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7707 SetUserThinkingEnables()
\r
7709 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7713 SetMachineThinkingEnables()
\r
7715 HMENU hMenu = GetMenu(hwndMain);
\r
7716 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7718 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7720 if (gameMode == MachinePlaysBlack) {
\r
7721 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7722 } else if (gameMode == MachinePlaysWhite) {
\r
7723 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7724 } else if (gameMode == TwoMachinesPlay) {
\r
7725 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7731 DisplayTitle(char *str)
\r
7733 char title[MSG_SIZ], *host;
\r
7734 if (str[0] != NULLCHAR) {
\r
7735 strcpy(title, str);
\r
7736 } else if (appData.icsActive) {
\r
7737 if (appData.icsCommPort[0] != NULLCHAR)
\r
7740 host = appData.icsHost;
\r
7741 sprintf(title, "%s: %s", szTitle, host);
\r
7742 } else if (appData.noChessProgram) {
\r
7743 strcpy(title, szTitle);
\r
7745 strcpy(title, szTitle);
\r
7746 strcat(title, ": ");
\r
7747 strcat(title, first.tidy);
\r
7749 SetWindowText(hwndMain, title);
\r
7754 DisplayMessage(char *str1, char *str2)
\r
7758 int remain = MESSAGE_TEXT_MAX - 1;
\r
7761 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7762 messageText[0] = NULLCHAR;
\r
7764 len = strlen(str1);
\r
7765 if (len > remain) len = remain;
\r
7766 strncpy(messageText, str1, len);
\r
7767 messageText[len] = NULLCHAR;
\r
7770 if (*str2 && remain >= 2) {
\r
7772 strcat(messageText, " ");
\r
7775 len = strlen(str2);
\r
7776 if (len > remain) len = remain;
\r
7777 strncat(messageText, str2, len);
\r
7779 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7781 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7785 hdc = GetDC(hwndMain);
\r
7786 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7787 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7788 &messageRect, messageText, strlen(messageText), NULL);
\r
7789 (void) SelectObject(hdc, oldFont);
\r
7790 (void) ReleaseDC(hwndMain, hdc);
\r
7794 DisplayError(char *str, int error)
\r
7796 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7802 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7803 NULL, error, LANG_NEUTRAL,
\r
7804 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7806 sprintf(buf, "%s:\n%s", str, buf2);
\r
7808 ErrorMap *em = errmap;
\r
7809 while (em->err != 0 && em->err != error) em++;
\r
7810 if (em->err != 0) {
\r
7811 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7813 sprintf(buf, "%s:\nError code %d", str, error);
\r
7818 ErrorPopUp("Error", buf);
\r
7823 DisplayMoveError(char *str)
\r
7825 fromX = fromY = -1;
\r
7826 ClearHighlights();
\r
7827 DrawPosition(FALSE, NULL);
\r
7828 if (appData.popupMoveErrors) {
\r
7829 ErrorPopUp("Error", str);
\r
7831 DisplayMessage(str, "");
\r
7832 moveErrorMessageUp = TRUE;
\r
7837 DisplayFatalError(char *str, int error, int exitStatus)
\r
7839 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7841 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7844 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7845 NULL, error, LANG_NEUTRAL,
\r
7846 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7848 sprintf(buf, "%s:\n%s", str, buf2);
\r
7850 ErrorMap *em = errmap;
\r
7851 while (em->err != 0 && em->err != error) em++;
\r
7852 if (em->err != 0) {
\r
7853 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7855 sprintf(buf, "%s:\nError code %d", str, error);
\r
7860 if (appData.debugMode) {
\r
7861 fprintf(debugFP, "%s: %s\n", label, str);
\r
7863 if (appData.popupExitMessage) {
\r
7864 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7865 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7867 ExitEvent(exitStatus);
\r
7872 DisplayInformation(char *str)
\r
7874 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7879 DisplayNote(char *str)
\r
7881 ErrorPopUp("Note", str);
\r
7886 char *title, *question, *replyPrefix;
\r
7891 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7893 static QuestionParams *qp;
\r
7894 char reply[MSG_SIZ];
\r
7897 switch (message) {
\r
7898 case WM_INITDIALOG:
\r
7899 qp = (QuestionParams *) lParam;
\r
7900 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7901 SetWindowText(hDlg, qp->title);
\r
7902 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7903 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7907 switch (LOWORD(wParam)) {
\r
7909 strcpy(reply, qp->replyPrefix);
\r
7910 if (*reply) strcat(reply, " ");
\r
7911 len = strlen(reply);
\r
7912 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7913 strcat(reply, "\n");
\r
7914 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7915 EndDialog(hDlg, TRUE);
\r
7916 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7919 EndDialog(hDlg, FALSE);
\r
7930 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7932 QuestionParams qp;
\r
7936 qp.question = question;
\r
7937 qp.replyPrefix = replyPrefix;
\r
7939 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7940 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7941 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7942 FreeProcInstance(lpProc);
\r
7945 /* [AS] Pick FRC position */
\r
7946 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7948 static int * lpIndexFRC;
\r
7954 case WM_INITDIALOG:
\r
7955 lpIndexFRC = (int *) lParam;
\r
7957 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7959 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7960 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7961 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7962 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7967 switch( LOWORD(wParam) ) {
\r
7969 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7970 EndDialog( hDlg, 0 );
\r
7971 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7974 EndDialog( hDlg, 1 );
\r
7976 case IDC_NFG_Edit:
\r
7977 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7978 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7980 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7983 case IDC_NFG_Random:
\r
7984 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7985 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7998 int index = appData.defaultFrcPosition;
\r
7999 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8001 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8003 if( result == 0 ) {
\r
8004 appData.defaultFrcPosition = index;
\r
8010 /* [AS] Game list options. Refactored by HGM */
\r
8012 HWND gameListOptionsDialog;
\r
8014 // low-level front-end: clear text edit / list widget
\r
8018 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8021 // low-level front-end: clear text edit / list widget
\r
8023 GLT_DeSelectList()
\r
8025 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8028 // low-level front-end: append line to text edit / list widget
\r
8030 GLT_AddToList( char *name )
\r
8033 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8037 // low-level front-end: get line from text edit / list widget
\r
8039 GLT_GetFromList( int index, char *name )
\r
8042 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8048 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8050 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8051 int idx2 = idx1 + delta;
\r
8052 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8054 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8057 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8058 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8059 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8060 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8064 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8068 case WM_INITDIALOG:
\r
8069 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8071 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8073 /* Initialize list */
\r
8074 GLT_TagsToList( lpUserGLT );
\r
8076 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8081 switch( LOWORD(wParam) ) {
\r
8084 EndDialog( hDlg, 0 );
\r
8087 EndDialog( hDlg, 1 );
\r
8090 case IDC_GLT_Default:
\r
8091 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8094 case IDC_GLT_Restore:
\r
8095 GLT_TagsToList( appData.gameListTags );
\r
8099 GLT_MoveSelection( hDlg, -1 );
\r
8102 case IDC_GLT_Down:
\r
8103 GLT_MoveSelection( hDlg, +1 );
\r
8113 int GameListOptions()
\r
8116 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8118 strcpy( lpUserGLT, appData.gameListTags );
\r
8120 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8122 if( result == 0 ) {
\r
8123 /* [AS] Memory leak here! */
\r
8124 appData.gameListTags = strdup( lpUserGLT );
\r
8131 DisplayIcsInteractionTitle(char *str)
\r
8133 char consoleTitle[MSG_SIZ];
\r
8135 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8136 SetWindowText(hwndConsole, consoleTitle);
\r
8140 DrawPosition(int fullRedraw, Board board)
\r
8142 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8145 void NotifyFrontendLogin()
\r
8148 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8154 fromX = fromY = -1;
\r
8155 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8156 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8157 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8158 dragInfo.lastpos = dragInfo.pos;
\r
8159 dragInfo.start.x = dragInfo.start.y = -1;
\r
8160 dragInfo.from = dragInfo.start;
\r
8162 DrawPosition(TRUE, NULL);
\r
8168 CommentPopUp(char *title, char *str)
\r
8170 HWND hwnd = GetActiveWindow();
\r
8171 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8173 SetActiveWindow(hwnd);
\r
8177 CommentPopDown(void)
\r
8179 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8180 if (commentDialog) {
\r
8181 ShowWindow(commentDialog, SW_HIDE);
\r
8183 commentUp = FALSE;
\r
8187 EditCommentPopUp(int index, char *title, char *str)
\r
8189 EitherCommentPopUp(index, title, str, TRUE);
\r
8196 MyPlaySound(&sounds[(int)SoundMove]);
\r
8199 VOID PlayIcsWinSound()
\r
8201 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8204 VOID PlayIcsLossSound()
\r
8206 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8209 VOID PlayIcsDrawSound()
\r
8211 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8214 VOID PlayIcsUnfinishedSound()
\r
8216 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8222 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8230 consoleEcho = TRUE;
\r
8231 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8232 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8233 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8242 consoleEcho = FALSE;
\r
8243 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8244 /* This works OK: set text and background both to the same color */
\r
8246 cf.crTextColor = COLOR_ECHOOFF;
\r
8247 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8248 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8251 /* No Raw()...? */
\r
8253 void Colorize(ColorClass cc, int continuation)
\r
8255 currentColorClass = cc;
\r
8256 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8257 consoleCF.crTextColor = textAttribs[cc].color;
\r
8258 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8259 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8265 static char buf[MSG_SIZ];
\r
8266 DWORD bufsiz = MSG_SIZ;
\r
8268 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8269 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8271 if (!GetUserName(buf, &bufsiz)) {
\r
8272 /*DisplayError("Error getting user name", GetLastError());*/
\r
8273 strcpy(buf, "User");
\r
8281 static char buf[MSG_SIZ];
\r
8282 DWORD bufsiz = MSG_SIZ;
\r
8284 if (!GetComputerName(buf, &bufsiz)) {
\r
8285 /*DisplayError("Error getting host name", GetLastError());*/
\r
8286 strcpy(buf, "Unknown");
\r
8293 ClockTimerRunning()
\r
8295 return clockTimerEvent != 0;
\r
8301 if (clockTimerEvent == 0) return FALSE;
\r
8302 KillTimer(hwndMain, clockTimerEvent);
\r
8303 clockTimerEvent = 0;
\r
8308 StartClockTimer(long millisec)
\r
8310 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8311 (UINT) millisec, NULL);
\r
8315 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8318 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8320 if(appData.noGUI) return;
\r
8321 hdc = GetDC(hwndMain);
\r
8322 if (!IsIconic(hwndMain)) {
\r
8323 DisplayAClock(hdc, timeRemaining, highlight,
\r
8324 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8326 if (highlight && iconCurrent == iconBlack) {
\r
8327 iconCurrent = iconWhite;
\r
8328 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8329 if (IsIconic(hwndMain)) {
\r
8330 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8333 (void) ReleaseDC(hwndMain, hdc);
\r
8335 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8339 DisplayBlackClock(long timeRemaining, int highlight)
\r
8342 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8344 if(appData.noGUI) return;
\r
8345 hdc = GetDC(hwndMain);
\r
8346 if (!IsIconic(hwndMain)) {
\r
8347 DisplayAClock(hdc, timeRemaining, highlight,
\r
8348 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8350 if (highlight && iconCurrent == iconWhite) {
\r
8351 iconCurrent = iconBlack;
\r
8352 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8353 if (IsIconic(hwndMain)) {
\r
8354 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8357 (void) ReleaseDC(hwndMain, hdc);
\r
8359 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8364 LoadGameTimerRunning()
\r
8366 return loadGameTimerEvent != 0;
\r
8370 StopLoadGameTimer()
\r
8372 if (loadGameTimerEvent == 0) return FALSE;
\r
8373 KillTimer(hwndMain, loadGameTimerEvent);
\r
8374 loadGameTimerEvent = 0;
\r
8379 StartLoadGameTimer(long millisec)
\r
8381 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8382 (UINT) millisec, NULL);
\r
8390 char fileTitle[MSG_SIZ];
\r
8392 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8393 f = OpenFileDialog(hwndMain, "a", defName,
\r
8394 appData.oldSaveStyle ? "gam" : "pgn",
\r
8396 "Save Game to File", NULL, fileTitle, NULL);
\r
8398 SaveGame(f, 0, "");
\r
8405 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8407 if (delayedTimerEvent != 0) {
\r
8408 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8409 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8411 KillTimer(hwndMain, delayedTimerEvent);
\r
8412 delayedTimerEvent = 0;
\r
8413 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8414 delayedTimerCallback();
\r
8416 delayedTimerCallback = cb;
\r
8417 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8418 (UINT) millisec, NULL);
\r
8421 DelayedEventCallback
\r
8424 if (delayedTimerEvent) {
\r
8425 return delayedTimerCallback;
\r
8432 CancelDelayedEvent()
\r
8434 if (delayedTimerEvent) {
\r
8435 KillTimer(hwndMain, delayedTimerEvent);
\r
8436 delayedTimerEvent = 0;
\r
8440 DWORD GetWin32Priority(int nice)
\r
8441 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8443 REALTIME_PRIORITY_CLASS 0x00000100
\r
8444 HIGH_PRIORITY_CLASS 0x00000080
\r
8445 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8446 NORMAL_PRIORITY_CLASS 0x00000020
\r
8447 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8448 IDLE_PRIORITY_CLASS 0x00000040
\r
8450 if (nice < -15) return 0x00000080;
\r
8451 if (nice < 0) return 0x00008000;
\r
8452 if (nice == 0) return 0x00000020;
\r
8453 if (nice < 15) return 0x00004000;
\r
8454 return 0x00000040;
\r
8457 /* Start a child process running the given program.
\r
8458 The process's standard output can be read from "from", and its
\r
8459 standard input can be written to "to".
\r
8460 Exit with fatal error if anything goes wrong.
\r
8461 Returns an opaque pointer that can be used to destroy the process
\r
8465 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8467 #define BUFSIZE 4096
\r
8469 HANDLE hChildStdinRd, hChildStdinWr,
\r
8470 hChildStdoutRd, hChildStdoutWr;
\r
8471 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8472 SECURITY_ATTRIBUTES saAttr;
\r
8474 PROCESS_INFORMATION piProcInfo;
\r
8475 STARTUPINFO siStartInfo;
\r
8477 char buf[MSG_SIZ];
\r
8480 if (appData.debugMode) {
\r
8481 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8486 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8487 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8488 saAttr.bInheritHandle = TRUE;
\r
8489 saAttr.lpSecurityDescriptor = NULL;
\r
8492 * The steps for redirecting child's STDOUT:
\r
8493 * 1. Create anonymous pipe to be STDOUT for child.
\r
8494 * 2. Create a noninheritable duplicate of read handle,
\r
8495 * and close the inheritable read handle.
\r
8498 /* Create a pipe for the child's STDOUT. */
\r
8499 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8500 return GetLastError();
\r
8503 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8504 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8505 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8506 FALSE, /* not inherited */
\r
8507 DUPLICATE_SAME_ACCESS);
\r
8509 return GetLastError();
\r
8511 CloseHandle(hChildStdoutRd);
\r
8514 * The steps for redirecting child's STDIN:
\r
8515 * 1. Create anonymous pipe to be STDIN for child.
\r
8516 * 2. Create a noninheritable duplicate of write handle,
\r
8517 * and close the inheritable write handle.
\r
8520 /* Create a pipe for the child's STDIN. */
\r
8521 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8522 return GetLastError();
\r
8525 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8526 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8527 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8528 FALSE, /* not inherited */
\r
8529 DUPLICATE_SAME_ACCESS);
\r
8531 return GetLastError();
\r
8533 CloseHandle(hChildStdinWr);
\r
8535 /* Arrange to (1) look in dir for the child .exe file, and
\r
8536 * (2) have dir be the child's working directory. Interpret
\r
8537 * dir relative to the directory WinBoard loaded from. */
\r
8538 GetCurrentDirectory(MSG_SIZ, buf);
\r
8539 SetCurrentDirectory(installDir);
\r
8540 SetCurrentDirectory(dir);
\r
8542 /* Now create the child process. */
\r
8544 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8545 siStartInfo.lpReserved = NULL;
\r
8546 siStartInfo.lpDesktop = NULL;
\r
8547 siStartInfo.lpTitle = NULL;
\r
8548 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8549 siStartInfo.cbReserved2 = 0;
\r
8550 siStartInfo.lpReserved2 = NULL;
\r
8551 siStartInfo.hStdInput = hChildStdinRd;
\r
8552 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8553 siStartInfo.hStdError = hChildStdoutWr;
\r
8555 fSuccess = CreateProcess(NULL,
\r
8556 cmdLine, /* command line */
\r
8557 NULL, /* process security attributes */
\r
8558 NULL, /* primary thread security attrs */
\r
8559 TRUE, /* handles are inherited */
\r
8560 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8561 NULL, /* use parent's environment */
\r
8563 &siStartInfo, /* STARTUPINFO pointer */
\r
8564 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8566 err = GetLastError();
\r
8567 SetCurrentDirectory(buf); /* return to prev directory */
\r
8572 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8573 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8574 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8577 /* Close the handles we don't need in the parent */
\r
8578 CloseHandle(piProcInfo.hThread);
\r
8579 CloseHandle(hChildStdinRd);
\r
8580 CloseHandle(hChildStdoutWr);
\r
8582 /* Prepare return value */
\r
8583 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8584 cp->kind = CPReal;
\r
8585 cp->hProcess = piProcInfo.hProcess;
\r
8586 cp->pid = piProcInfo.dwProcessId;
\r
8587 cp->hFrom = hChildStdoutRdDup;
\r
8588 cp->hTo = hChildStdinWrDup;
\r
8590 *pr = (void *) cp;
\r
8592 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8593 2000 where engines sometimes don't see the initial command(s)
\r
8594 from WinBoard and hang. I don't understand how that can happen,
\r
8595 but the Sleep is harmless, so I've put it in. Others have also
\r
8596 reported what may be the same problem, so hopefully this will fix
\r
8597 it for them too. */
\r
8605 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8607 ChildProc *cp; int result;
\r
8609 cp = (ChildProc *) pr;
\r
8610 if (cp == NULL) return;
\r
8612 switch (cp->kind) {
\r
8614 /* TerminateProcess is considered harmful, so... */
\r
8615 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8616 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8617 /* The following doesn't work because the chess program
\r
8618 doesn't "have the same console" as WinBoard. Maybe
\r
8619 we could arrange for this even though neither WinBoard
\r
8620 nor the chess program uses a console for stdio? */
\r
8621 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8623 /* [AS] Special termination modes for misbehaving programs... */
\r
8624 if( signal == 9 ) {
\r
8625 result = TerminateProcess( cp->hProcess, 0 );
\r
8627 if ( appData.debugMode) {
\r
8628 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8631 else if( signal == 10 ) {
\r
8632 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8634 if( dw != WAIT_OBJECT_0 ) {
\r
8635 result = TerminateProcess( cp->hProcess, 0 );
\r
8637 if ( appData.debugMode) {
\r
8638 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8644 CloseHandle(cp->hProcess);
\r
8648 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8652 closesocket(cp->sock);
\r
8657 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8658 closesocket(cp->sock);
\r
8659 closesocket(cp->sock2);
\r
8667 InterruptChildProcess(ProcRef pr)
\r
8671 cp = (ChildProc *) pr;
\r
8672 if (cp == NULL) return;
\r
8673 switch (cp->kind) {
\r
8675 /* The following doesn't work because the chess program
\r
8676 doesn't "have the same console" as WinBoard. Maybe
\r
8677 we could arrange for this even though neither WinBoard
\r
8678 nor the chess program uses a console for stdio */
\r
8679 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8684 /* Can't interrupt */
\r
8688 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8695 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8697 char cmdLine[MSG_SIZ];
\r
8699 if (port[0] == NULLCHAR) {
\r
8700 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8702 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8704 return StartChildProcess(cmdLine, "", pr);
\r
8708 /* Code to open TCP sockets */
\r
8711 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8716 struct sockaddr_in sa, mysa;
\r
8717 struct hostent FAR *hp;
\r
8718 unsigned short uport;
\r
8719 WORD wVersionRequested;
\r
8722 /* Initialize socket DLL */
\r
8723 wVersionRequested = MAKEWORD(1, 1);
\r
8724 err = WSAStartup(wVersionRequested, &wsaData);
\r
8725 if (err != 0) return err;
\r
8728 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8729 err = WSAGetLastError();
\r
8734 /* Bind local address using (mostly) don't-care values.
\r
8736 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8737 mysa.sin_family = AF_INET;
\r
8738 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8739 uport = (unsigned short) 0;
\r
8740 mysa.sin_port = htons(uport);
\r
8741 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8742 == SOCKET_ERROR) {
\r
8743 err = WSAGetLastError();
\r
8748 /* Resolve remote host name */
\r
8749 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8750 if (!(hp = gethostbyname(host))) {
\r
8751 unsigned int b0, b1, b2, b3;
\r
8753 err = WSAGetLastError();
\r
8755 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8756 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8757 hp->h_addrtype = AF_INET;
\r
8759 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8760 hp->h_addr_list[0] = (char *) malloc(4);
\r
8761 hp->h_addr_list[0][0] = (char) b0;
\r
8762 hp->h_addr_list[0][1] = (char) b1;
\r
8763 hp->h_addr_list[0][2] = (char) b2;
\r
8764 hp->h_addr_list[0][3] = (char) b3;
\r
8770 sa.sin_family = hp->h_addrtype;
\r
8771 uport = (unsigned short) atoi(port);
\r
8772 sa.sin_port = htons(uport);
\r
8773 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8775 /* Make connection */
\r
8776 if (connect(s, (struct sockaddr *) &sa,
\r
8777 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8778 err = WSAGetLastError();
\r
8783 /* Prepare return value */
\r
8784 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8785 cp->kind = CPSock;
\r
8787 *pr = (ProcRef *) cp;
\r
8793 OpenCommPort(char *name, ProcRef *pr)
\r
8798 char fullname[MSG_SIZ];
\r
8800 if (*name != '\\')
\r
8801 sprintf(fullname, "\\\\.\\%s", name);
\r
8803 strcpy(fullname, name);
\r
8805 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8806 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8807 if (h == (HANDLE) -1) {
\r
8808 return GetLastError();
\r
8812 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8814 /* Accumulate characters until a 100ms pause, then parse */
\r
8815 ct.ReadIntervalTimeout = 100;
\r
8816 ct.ReadTotalTimeoutMultiplier = 0;
\r
8817 ct.ReadTotalTimeoutConstant = 0;
\r
8818 ct.WriteTotalTimeoutMultiplier = 0;
\r
8819 ct.WriteTotalTimeoutConstant = 0;
\r
8820 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8822 /* Prepare return value */
\r
8823 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8824 cp->kind = CPComm;
\r
8827 *pr = (ProcRef *) cp;
\r
8833 OpenLoopback(ProcRef *pr)
\r
8835 DisplayFatalError("Not implemented", 0, 1);
\r
8841 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8846 struct sockaddr_in sa, mysa;
\r
8847 struct hostent FAR *hp;
\r
8848 unsigned short uport;
\r
8849 WORD wVersionRequested;
\r
8852 char stderrPortStr[MSG_SIZ];
\r
8854 /* Initialize socket DLL */
\r
8855 wVersionRequested = MAKEWORD(1, 1);
\r
8856 err = WSAStartup(wVersionRequested, &wsaData);
\r
8857 if (err != 0) return err;
\r
8859 /* Resolve remote host name */
\r
8860 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8861 if (!(hp = gethostbyname(host))) {
\r
8862 unsigned int b0, b1, b2, b3;
\r
8864 err = WSAGetLastError();
\r
8866 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8867 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8868 hp->h_addrtype = AF_INET;
\r
8870 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8871 hp->h_addr_list[0] = (char *) malloc(4);
\r
8872 hp->h_addr_list[0][0] = (char) b0;
\r
8873 hp->h_addr_list[0][1] = (char) b1;
\r
8874 hp->h_addr_list[0][2] = (char) b2;
\r
8875 hp->h_addr_list[0][3] = (char) b3;
\r
8881 sa.sin_family = hp->h_addrtype;
\r
8882 uport = (unsigned short) 514;
\r
8883 sa.sin_port = htons(uport);
\r
8884 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8886 /* Bind local socket to unused "privileged" port address
\r
8888 s = INVALID_SOCKET;
\r
8889 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8890 mysa.sin_family = AF_INET;
\r
8891 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8892 for (fromPort = 1023;; fromPort--) {
\r
8893 if (fromPort < 0) {
\r
8895 return WSAEADDRINUSE;
\r
8897 if (s == INVALID_SOCKET) {
\r
8898 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8899 err = WSAGetLastError();
\r
8904 uport = (unsigned short) fromPort;
\r
8905 mysa.sin_port = htons(uport);
\r
8906 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8907 == SOCKET_ERROR) {
\r
8908 err = WSAGetLastError();
\r
8909 if (err == WSAEADDRINUSE) continue;
\r
8913 if (connect(s, (struct sockaddr *) &sa,
\r
8914 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8915 err = WSAGetLastError();
\r
8916 if (err == WSAEADDRINUSE) {
\r
8927 /* Bind stderr local socket to unused "privileged" port address
\r
8929 s2 = INVALID_SOCKET;
\r
8930 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8931 mysa.sin_family = AF_INET;
\r
8932 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8933 for (fromPort = 1023;; fromPort--) {
\r
8934 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8935 if (fromPort < 0) {
\r
8936 (void) closesocket(s);
\r
8938 return WSAEADDRINUSE;
\r
8940 if (s2 == INVALID_SOCKET) {
\r
8941 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8942 err = WSAGetLastError();
\r
8948 uport = (unsigned short) fromPort;
\r
8949 mysa.sin_port = htons(uport);
\r
8950 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8951 == SOCKET_ERROR) {
\r
8952 err = WSAGetLastError();
\r
8953 if (err == WSAEADDRINUSE) continue;
\r
8954 (void) closesocket(s);
\r
8958 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8959 err = WSAGetLastError();
\r
8960 if (err == WSAEADDRINUSE) {
\r
8962 s2 = INVALID_SOCKET;
\r
8965 (void) closesocket(s);
\r
8966 (void) closesocket(s2);
\r
8972 prevStderrPort = fromPort; // remember port used
\r
8973 sprintf(stderrPortStr, "%d", fromPort);
\r
8975 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8976 err = WSAGetLastError();
\r
8977 (void) closesocket(s);
\r
8978 (void) closesocket(s2);
\r
8983 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8984 err = WSAGetLastError();
\r
8985 (void) closesocket(s);
\r
8986 (void) closesocket(s2);
\r
8990 if (*user == NULLCHAR) user = UserName();
\r
8991 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8992 err = WSAGetLastError();
\r
8993 (void) closesocket(s);
\r
8994 (void) closesocket(s2);
\r
8998 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8999 err = WSAGetLastError();
\r
9000 (void) closesocket(s);
\r
9001 (void) closesocket(s2);
\r
9006 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9007 err = WSAGetLastError();
\r
9008 (void) closesocket(s);
\r
9009 (void) closesocket(s2);
\r
9013 (void) closesocket(s2); /* Stop listening */
\r
9015 /* Prepare return value */
\r
9016 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9017 cp->kind = CPRcmd;
\r
9020 *pr = (ProcRef *) cp;
\r
9027 AddInputSource(ProcRef pr, int lineByLine,
\r
9028 InputCallback func, VOIDSTAR closure)
\r
9030 InputSource *is, *is2 = NULL;
\r
9031 ChildProc *cp = (ChildProc *) pr;
\r
9033 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9034 is->lineByLine = lineByLine;
\r
9036 is->closure = closure;
\r
9037 is->second = NULL;
\r
9038 is->next = is->buf;
\r
9039 if (pr == NoProc) {
\r
9040 is->kind = CPReal;
\r
9041 consoleInputSource = is;
\r
9043 is->kind = cp->kind;
\r
9045 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9046 we create all threads suspended so that the is->hThread variable can be
\r
9047 safely assigned, then let the threads start with ResumeThread.
\r
9049 switch (cp->kind) {
\r
9051 is->hFile = cp->hFrom;
\r
9052 cp->hFrom = NULL; /* now owned by InputThread */
\r
9054 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9055 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9059 is->hFile = cp->hFrom;
\r
9060 cp->hFrom = NULL; /* now owned by InputThread */
\r
9062 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9063 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9067 is->sock = cp->sock;
\r
9069 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9070 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9074 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9076 is->sock = cp->sock;
\r
9078 is2->sock = cp->sock2;
\r
9079 is2->second = is2;
\r
9081 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9082 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9084 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9085 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9089 if( is->hThread != NULL ) {
\r
9090 ResumeThread( is->hThread );
\r
9093 if( is2 != NULL && is2->hThread != NULL ) {
\r
9094 ResumeThread( is2->hThread );
\r
9098 return (InputSourceRef) is;
\r
9102 RemoveInputSource(InputSourceRef isr)
\r
9106 is = (InputSource *) isr;
\r
9107 is->hThread = NULL; /* tell thread to stop */
\r
9108 CloseHandle(is->hThread);
\r
9109 if (is->second != NULL) {
\r
9110 is->second->hThread = NULL;
\r
9111 CloseHandle(is->second->hThread);
\r
9115 int no_wrap(char *message, int count)
\r
9117 ConsoleOutput(message, count, FALSE);
\r
9122 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9125 int outCount = SOCKET_ERROR;
\r
9126 ChildProc *cp = (ChildProc *) pr;
\r
9127 static OVERLAPPED ovl;
\r
9128 static int line = 0;
\r
9132 if (appData.noJoin || !appData.useInternalWrap)
\r
9133 return no_wrap(message, count);
\r
9136 int width = get_term_width();
\r
9137 int len = wrap(NULL, message, count, width, &line);
\r
9138 char *msg = malloc(len);
\r
9142 return no_wrap(message, count);
\r
9145 dbgchk = wrap(msg, message, count, width, &line);
\r
9146 if (dbgchk != len && appData.debugMode)
\r
9147 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9148 ConsoleOutput(msg, len, FALSE);
\r
9155 if (ovl.hEvent == NULL) {
\r
9156 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9158 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9160 switch (cp->kind) {
\r
9163 outCount = send(cp->sock, message, count, 0);
\r
9164 if (outCount == SOCKET_ERROR) {
\r
9165 *outError = WSAGetLastError();
\r
9167 *outError = NO_ERROR;
\r
9172 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9173 &dOutCount, NULL)) {
\r
9174 *outError = NO_ERROR;
\r
9175 outCount = (int) dOutCount;
\r
9177 *outError = GetLastError();
\r
9182 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9183 &dOutCount, &ovl);
\r
9184 if (*outError == NO_ERROR) {
\r
9185 outCount = (int) dOutCount;
\r
9193 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9196 /* Ignore delay, not implemented for WinBoard */
\r
9197 return OutputToProcess(pr, message, count, outError);
\r
9202 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9203 char *buf, int count, int error)
\r
9205 DisplayFatalError("Not implemented", 0, 1);
\r
9208 /* see wgamelist.c for Game List functions */
\r
9209 /* see wedittags.c for Edit Tags functions */
\r
9216 char buf[MSG_SIZ];
\r
9219 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9220 f = fopen(buf, "r");
\r
9222 ProcessICSInitScript(f);
\r
9230 StartAnalysisClock()
\r
9232 if (analysisTimerEvent) return;
\r
9233 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9234 (UINT) 2000, NULL);
\r
9238 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9240 highlightInfo.sq[0].x = fromX;
\r
9241 highlightInfo.sq[0].y = fromY;
\r
9242 highlightInfo.sq[1].x = toX;
\r
9243 highlightInfo.sq[1].y = toY;
\r
9249 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9250 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9254 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9256 premoveHighlightInfo.sq[0].x = fromX;
\r
9257 premoveHighlightInfo.sq[0].y = fromY;
\r
9258 premoveHighlightInfo.sq[1].x = toX;
\r
9259 premoveHighlightInfo.sq[1].y = toY;
\r
9263 ClearPremoveHighlights()
\r
9265 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9266 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9270 ShutDownFrontEnd()
\r
9272 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9273 DeleteClipboardTempFiles();
\r
9279 if (IsIconic(hwndMain))
\r
9280 ShowWindow(hwndMain, SW_RESTORE);
\r
9282 SetActiveWindow(hwndMain);
\r
9286 * Prototypes for animation support routines
\r
9288 static void ScreenSquare(int column, int row, POINT * pt);
\r
9289 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9290 POINT frames[], int * nFrames);
\r
9294 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9295 { // [HGM] atomic: animate blast wave
\r
9297 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9298 explodeInfo.fromX = fromX;
\r
9299 explodeInfo.fromY = fromY;
\r
9300 explodeInfo.toX = toX;
\r
9301 explodeInfo.toY = toY;
\r
9302 for(i=1; i<nFrames; i++) {
\r
9303 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9304 DrawPosition(FALSE, NULL);
\r
9305 Sleep(appData.animSpeed);
\r
9307 explodeInfo.radius = 0;
\r
9308 DrawPosition(TRUE, NULL);
\r
9314 AnimateMove(board, fromX, fromY, toX, toY)
\r
9321 ChessSquare piece;
\r
9322 POINT start, finish, mid;
\r
9323 POINT frames[kFactor * 2 + 1];
\r
9326 if (!appData.animate) return;
\r
9327 if (doingSizing) return;
\r
9328 if (fromY < 0 || fromX < 0) return;
\r
9329 piece = board[fromY][fromX];
\r
9330 if (piece >= EmptySquare) return;
\r
9332 ScreenSquare(fromX, fromY, &start);
\r
9333 ScreenSquare(toX, toY, &finish);
\r
9335 /* All pieces except knights move in straight line */
\r
9336 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9337 mid.x = start.x + (finish.x - start.x) / 2;
\r
9338 mid.y = start.y + (finish.y - start.y) / 2;
\r
9340 /* Knight: make diagonal movement then straight */
\r
9341 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9342 mid.x = start.x + (finish.x - start.x) / 2;
\r
9346 mid.y = start.y + (finish.y - start.y) / 2;
\r
9350 /* Don't use as many frames for very short moves */
\r
9351 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9352 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9354 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9356 animInfo.from.x = fromX;
\r
9357 animInfo.from.y = fromY;
\r
9358 animInfo.to.x = toX;
\r
9359 animInfo.to.y = toY;
\r
9360 animInfo.lastpos = start;
\r
9361 animInfo.piece = piece;
\r
9362 for (n = 0; n < nFrames; n++) {
\r
9363 animInfo.pos = frames[n];
\r
9364 DrawPosition(FALSE, NULL);
\r
9365 animInfo.lastpos = animInfo.pos;
\r
9366 Sleep(appData.animSpeed);
\r
9368 animInfo.pos = finish;
\r
9369 DrawPosition(FALSE, NULL);
\r
9370 animInfo.piece = EmptySquare;
\r
9371 if(gameInfo.variant == VariantAtomic &&
\r
9372 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9373 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9376 /* Convert board position to corner of screen rect and color */
\r
9379 ScreenSquare(column, row, pt)
\r
9380 int column; int row; POINT * pt;
\r
9383 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9384 pt->y = lineGap + row * (squareSize + lineGap);
\r
9386 pt->x = lineGap + column * (squareSize + lineGap);
\r
9387 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9391 /* Generate a series of frame coords from start->mid->finish.
\r
9392 The movement rate doubles until the half way point is
\r
9393 reached, then halves back down to the final destination,
\r
9394 which gives a nice slow in/out effect. The algorithmn
\r
9395 may seem to generate too many intermediates for short
\r
9396 moves, but remember that the purpose is to attract the
\r
9397 viewers attention to the piece about to be moved and
\r
9398 then to where it ends up. Too few frames would be less
\r
9402 Tween(start, mid, finish, factor, frames, nFrames)
\r
9403 POINT * start; POINT * mid;
\r
9404 POINT * finish; int factor;
\r
9405 POINT frames[]; int * nFrames;
\r
9407 int n, fraction = 1, count = 0;
\r
9409 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9410 for (n = 0; n < factor; n++)
\r
9412 for (n = 0; n < factor; n++) {
\r
9413 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9414 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9416 fraction = fraction / 2;
\r
9420 frames[count] = *mid;
\r
9423 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9425 for (n = 0; n < factor; n++) {
\r
9426 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9427 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9429 fraction = fraction * 2;
\r
9435 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9437 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9439 EvalGraphSet( first, last, current, pvInfoList );
\r