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 == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3801 AdjustClock(flipClock, -1);
\r
3802 } else if (gameMode == IcsPlayingBlack ||
\r
3803 gameMode == MachinePlaysWhite) {
\r
3806 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3807 if (gameMode == EditPosition) {
\r
3808 SetBlackToPlayEvent();
\r
3809 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3810 AdjustClock(!flipClock, -1);
\r
3811 } else if (gameMode == IcsPlayingWhite ||
\r
3812 gameMode == MachinePlaysBlack) {
\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 || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
3894 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3895 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
3899 DrawPosition(TRUE, NULL);
\r
3901 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3904 if (message == WM_MBUTTONDOWN) {
\r
3905 buttonCount = 3; /* even if system didn't think so */
\r
3906 if (wParam & MK_SHIFT)
\r
3907 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3909 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3910 } else { /* message == WM_RBUTTONDOWN */
\r
3911 /* Just have one menu, on the right button. Windows users don't
\r
3912 think to try the middle one, and sometimes other software steals
\r
3913 it, or it doesn't really exist. */
\r
3914 if(gameInfo.variant != VariantShogi)
\r
3915 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3917 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3921 SetCapture(hwndMain);
3924 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3925 SetupDropMenu(hmenu);
\r
3926 MenuPopup(hwnd, pt, hmenu, -1);
\r
3936 /* Preprocess messages for buttons in main window */
\r
3938 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3940 int id = GetWindowLong(hwnd, GWL_ID);
\r
3943 for (i=0; i<N_BUTTONS; i++) {
\r
3944 if (buttonDesc[i].id == id) break;
\r
3946 if (i == N_BUTTONS) return 0;
\r
3947 switch (message) {
\r
3952 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3953 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3960 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3963 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3964 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3965 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3966 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3968 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3970 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3971 PopUpMoveDialog((char)wParam);
\r
3977 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3980 /* Process messages for Promotion dialog box */
\r
3982 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3986 switch (message) {
\r
3987 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3988 /* Center the dialog over the application window */
\r
3989 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3990 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3991 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3992 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3993 SW_SHOW : SW_HIDE);
\r
3994 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3995 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3996 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3997 PieceToChar(WhiteAngel) != '~') ||
\r
3998 (PieceToChar(BlackAngel) >= 'A' &&
\r
3999 PieceToChar(BlackAngel) != '~') ) ?
\r
4000 SW_SHOW : SW_HIDE);
\r
4001 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4002 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4003 PieceToChar(WhiteMarshall) != '~') ||
\r
4004 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4005 PieceToChar(BlackMarshall) != '~') ) ?
\r
4006 SW_SHOW : SW_HIDE);
\r
4007 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4008 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4009 gameInfo.variant != VariantShogi ?
\r
4010 SW_SHOW : SW_HIDE);
\r
4011 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4012 gameInfo.variant != VariantShogi ?
\r
4013 SW_SHOW : SW_HIDE);
\r
4014 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4015 gameInfo.variant == VariantShogi ?
\r
4016 SW_SHOW : SW_HIDE);
\r
4017 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4018 gameInfo.variant == VariantShogi ?
\r
4019 SW_SHOW : SW_HIDE);
\r
4020 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4021 gameInfo.variant == VariantSuper ?
\r
4022 SW_SHOW : SW_HIDE);
\r
4025 case WM_COMMAND: /* message: received a command */
\r
4026 switch (LOWORD(wParam)) {
\r
4028 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4029 ClearHighlights();
\r
4030 DrawPosition(FALSE, NULL);
\r
4033 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4036 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4039 promoChar = PieceToChar(BlackRook);
\r
4042 promoChar = PieceToChar(BlackBishop);
\r
4044 case PB_Chancellor:
\r
4045 promoChar = PieceToChar(BlackMarshall);
\r
4047 case PB_Archbishop:
\r
4048 promoChar = PieceToChar(BlackAngel);
\r
4051 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4056 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4057 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4058 only show the popup when we are already sure the move is valid or
\r
4059 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4060 will figure out it is a promotion from the promoChar. */
\r
4061 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4062 fromX = fromY = -1;
\r
4063 if (!appData.highlightLastMove) {
\r
4064 ClearHighlights();
\r
4065 DrawPosition(FALSE, NULL);
\r
4072 /* Pop up promotion dialog */
\r
4074 PromotionPopup(HWND hwnd)
\r
4078 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4079 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4080 hwnd, (DLGPROC)lpProc);
\r
4081 FreeProcInstance(lpProc);
\r
4087 DrawPosition(TRUE, NULL);
\r
4088 PromotionPopup(hwndMain);
\r
4091 /* Toggle ShowThinking */
\r
4093 ToggleShowThinking()
\r
4095 appData.showThinking = !appData.showThinking;
\r
4096 ShowThinkingEvent();
\r
4100 LoadGameDialog(HWND hwnd, char* title)
\r
4104 char fileTitle[MSG_SIZ];
\r
4105 f = OpenFileDialog(hwnd, "rb", "",
\r
4106 appData.oldSaveStyle ? "gam" : "pgn",
\r
4108 title, &number, fileTitle, NULL);
\r
4110 cmailMsgLoaded = FALSE;
\r
4111 if (number == 0) {
\r
4112 int error = GameListBuild(f);
\r
4114 DisplayError("Cannot build game list", error);
\r
4115 } else if (!ListEmpty(&gameList) &&
\r
4116 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4117 GameListPopUp(f, fileTitle);
\r
4120 GameListDestroy();
\r
4123 LoadGame(f, number, fileTitle, FALSE);
\r
4127 int get_term_width()
\r
4132 HFONT hfont, hold_font;
\r
4137 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4141 // get the text metrics
\r
4142 hdc = GetDC(hText);
\r
4143 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4144 if (consoleCF.dwEffects & CFE_BOLD)
\r
4145 lf.lfWeight = FW_BOLD;
\r
4146 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4147 lf.lfItalic = TRUE;
\r
4148 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4149 lf.lfStrikeOut = TRUE;
\r
4150 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4151 lf.lfUnderline = TRUE;
\r
4152 hfont = CreateFontIndirect(&lf);
\r
4153 hold_font = SelectObject(hdc, hfont);
\r
4154 GetTextMetrics(hdc, &tm);
\r
4155 SelectObject(hdc, hold_font);
\r
4156 DeleteObject(hfont);
\r
4157 ReleaseDC(hText, hdc);
\r
4159 // get the rectangle
\r
4160 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4162 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4165 void UpdateICSWidth(HWND hText)
\r
4167 LONG old_width, new_width;
\r
4169 new_width = get_term_width(hText, FALSE);
\r
4170 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4171 if (new_width != old_width)
\r
4173 ics_update_width(new_width);
\r
4174 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4179 ChangedConsoleFont()
\r
4182 CHARRANGE tmpsel, sel;
\r
4183 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4184 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4185 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4188 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4189 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4190 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4191 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4192 * size. This was undocumented in the version of MSVC++ that I had
\r
4193 * when I wrote the code, but is apparently documented now.
\r
4195 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4196 cfmt.bCharSet = f->lf.lfCharSet;
\r
4197 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4198 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4199 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4200 /* Why are the following seemingly needed too? */
\r
4201 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4202 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4203 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4205 tmpsel.cpMax = -1; /*999999?*/
\r
4206 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4207 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4208 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4209 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4211 paraf.cbSize = sizeof(paraf);
\r
4212 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4213 paraf.dxStartIndent = 0;
\r
4214 paraf.dxOffset = WRAP_INDENT;
\r
4215 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4216 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4217 UpdateICSWidth(hText);
\r
4220 /*---------------------------------------------------------------------------*\
\r
4222 * Window Proc for main window
\r
4224 \*---------------------------------------------------------------------------*/
\r
4226 /* Process messages for main window, etc. */
\r
4228 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4231 int wmId, wmEvent;
\r
4235 char fileTitle[MSG_SIZ];
\r
4236 char buf[MSG_SIZ];
\r
4237 static SnapData sd;
\r
4239 switch (message) {
\r
4241 case WM_PAINT: /* message: repaint portion of window */
\r
4245 case WM_ERASEBKGND:
\r
4246 if (IsIconic(hwnd)) {
\r
4247 /* Cheat; change the message */
\r
4248 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4250 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4254 case WM_LBUTTONDOWN:
\r
4255 case WM_MBUTTONDOWN:
\r
4256 case WM_RBUTTONDOWN:
\r
4257 case WM_LBUTTONUP:
\r
4258 case WM_MBUTTONUP:
\r
4259 case WM_RBUTTONUP:
\r
4260 case WM_MOUSEMOVE:
\r
4261 case WM_MOUSEWHEEL:
\r
4262 MouseEvent(hwnd, message, wParam, lParam);
\r
4265 JAWS_KB_NAVIGATION
\r
4269 JAWS_ALT_INTERCEPT
\r
4271 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4272 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4273 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4274 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4276 SendMessage(h, message, wParam, lParam);
\r
4277 } else if(lParam != KF_REPEAT) {
\r
4278 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4279 PopUpMoveDialog((char)wParam);
\r
4280 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4281 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4286 case WM_PALETTECHANGED:
\r
4287 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4289 HDC hdc = GetDC(hwndMain);
\r
4290 SelectPalette(hdc, hPal, TRUE);
\r
4291 nnew = RealizePalette(hdc);
\r
4293 paletteChanged = TRUE;
\r
4294 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4296 ReleaseDC(hwnd, hdc);
\r
4300 case WM_QUERYNEWPALETTE:
\r
4301 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4303 HDC hdc = GetDC(hwndMain);
\r
4304 paletteChanged = FALSE;
\r
4305 SelectPalette(hdc, hPal, FALSE);
\r
4306 nnew = RealizePalette(hdc);
\r
4308 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4310 ReleaseDC(hwnd, hdc);
\r
4315 case WM_COMMAND: /* message: command from application menu */
\r
4316 wmId = LOWORD(wParam);
\r
4317 wmEvent = HIWORD(wParam);
\r
4322 SAY("new game enter a move to play against the computer with white");
\r
4325 case IDM_NewGameFRC:
\r
4326 if( NewGameFRC() == 0 ) {
\r
4331 case IDM_NewVariant:
\r
4332 NewVariantPopup(hwnd);
\r
4335 case IDM_LoadGame:
\r
4336 LoadGameDialog(hwnd, "Load Game from File");
\r
4339 case IDM_LoadNextGame:
\r
4343 case IDM_LoadPrevGame:
\r
4347 case IDM_ReloadGame:
\r
4351 case IDM_LoadPosition:
\r
4352 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4353 Reset(FALSE, TRUE);
\r
4356 f = OpenFileDialog(hwnd, "rb", "",
\r
4357 appData.oldSaveStyle ? "pos" : "fen",
\r
4359 "Load Position from File", &number, fileTitle, NULL);
\r
4361 LoadPosition(f, number, fileTitle);
\r
4365 case IDM_LoadNextPosition:
\r
4366 ReloadPosition(1);
\r
4369 case IDM_LoadPrevPosition:
\r
4370 ReloadPosition(-1);
\r
4373 case IDM_ReloadPosition:
\r
4374 ReloadPosition(0);
\r
4377 case IDM_SaveGame:
\r
4378 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4379 f = OpenFileDialog(hwnd, "a", defName,
\r
4380 appData.oldSaveStyle ? "gam" : "pgn",
\r
4382 "Save Game to File", NULL, fileTitle, NULL);
\r
4384 SaveGame(f, 0, "");
\r
4388 case IDM_SavePosition:
\r
4389 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4390 f = OpenFileDialog(hwnd, "a", defName,
\r
4391 appData.oldSaveStyle ? "pos" : "fen",
\r
4393 "Save Position to File", NULL, fileTitle, NULL);
\r
4395 SavePosition(f, 0, "");
\r
4399 case IDM_SaveDiagram:
\r
4400 defName = "diagram";
\r
4401 f = OpenFileDialog(hwnd, "wb", defName,
\r
4404 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4410 case IDM_CopyGame:
\r
4411 CopyGameToClipboard();
\r
4414 case IDM_PasteGame:
\r
4415 PasteGameFromClipboard();
\r
4418 case IDM_CopyGameListToClipboard:
\r
4419 CopyGameListToClipboard();
\r
4422 /* [AS] Autodetect FEN or PGN data */
\r
4423 case IDM_PasteAny:
\r
4424 PasteGameOrFENFromClipboard();
\r
4427 /* [AS] Move history */
\r
4428 case IDM_ShowMoveHistory:
\r
4429 if( MoveHistoryIsUp() ) {
\r
4430 MoveHistoryPopDown();
\r
4433 MoveHistoryPopUp();
\r
4437 /* [AS] Eval graph */
\r
4438 case IDM_ShowEvalGraph:
\r
4439 if( EvalGraphIsUp() ) {
\r
4440 EvalGraphPopDown();
\r
4444 SetFocus(hwndMain);
\r
4448 /* [AS] Engine output */
\r
4449 case IDM_ShowEngineOutput:
\r
4450 if( EngineOutputIsUp() ) {
\r
4451 EngineOutputPopDown();
\r
4454 EngineOutputPopUp();
\r
4458 /* [AS] User adjudication */
\r
4459 case IDM_UserAdjudication_White:
\r
4460 UserAdjudicationEvent( +1 );
\r
4463 case IDM_UserAdjudication_Black:
\r
4464 UserAdjudicationEvent( -1 );
\r
4467 case IDM_UserAdjudication_Draw:
\r
4468 UserAdjudicationEvent( 0 );
\r
4471 /* [AS] Game list options dialog */
\r
4472 case IDM_GameListOptions:
\r
4473 GameListOptions();
\r
4480 case IDM_CopyPosition:
\r
4481 CopyFENToClipboard();
\r
4484 case IDM_PastePosition:
\r
4485 PasteFENFromClipboard();
\r
4488 case IDM_MailMove:
\r
4492 case IDM_ReloadCMailMsg:
\r
4493 Reset(TRUE, TRUE);
\r
4494 ReloadCmailMsgEvent(FALSE);
\r
4497 case IDM_Minimize:
\r
4498 ShowWindow(hwnd, SW_MINIMIZE);
\r
4505 case IDM_MachineWhite:
\r
4506 MachineWhiteEvent();
\r
4508 * refresh the tags dialog only if it's visible
\r
4510 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4512 tags = PGNTags(&gameInfo);
\r
4513 TagsPopUp(tags, CmailMsg());
\r
4516 SAY("computer starts playing white");
\r
4519 case IDM_MachineBlack:
\r
4520 MachineBlackEvent();
\r
4522 * refresh the tags dialog only if it's visible
\r
4524 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4526 tags = PGNTags(&gameInfo);
\r
4527 TagsPopUp(tags, CmailMsg());
\r
4530 SAY("computer starts playing black");
\r
4533 case IDM_TwoMachines:
\r
4534 TwoMachinesEvent();
\r
4536 * refresh the tags dialog only if it's visible
\r
4538 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4540 tags = PGNTags(&gameInfo);
\r
4541 TagsPopUp(tags, CmailMsg());
\r
4544 SAY("programs start playing each other");
\r
4547 case IDM_AnalysisMode:
\r
4548 if (!first.analysisSupport) {
\r
4549 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4550 DisplayError(buf, 0);
\r
4552 SAY("analyzing current position");
\r
4553 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4554 if (appData.icsActive) {
\r
4555 if (gameMode != IcsObserving) {
\r
4556 sprintf(buf, "You are not observing a game");
\r
4557 DisplayError(buf, 0);
\r
4558 /* secure check */
\r
4559 if (appData.icsEngineAnalyze) {
\r
4560 if (appData.debugMode)
\r
4561 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4562 ExitAnalyzeMode();
\r
4568 /* if enable, user want disable icsEngineAnalyze */
\r
4569 if (appData.icsEngineAnalyze) {
\r
4570 ExitAnalyzeMode();
\r
4574 appData.icsEngineAnalyze = TRUE;
\r
4575 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4578 if (!appData.showThinking) ToggleShowThinking();
\r
4579 AnalyzeModeEvent();
\r
4583 case IDM_AnalyzeFile:
\r
4584 if (!first.analysisSupport) {
\r
4585 char buf[MSG_SIZ];
\r
4586 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4587 DisplayError(buf, 0);
\r
4589 if (!appData.showThinking) ToggleShowThinking();
\r
4590 AnalyzeFileEvent();
\r
4591 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4592 AnalysisPeriodicEvent(1);
\r
4596 case IDM_IcsClient:
\r
4600 case IDM_EditGame:
\r
4605 case IDM_EditPosition:
\r
4606 EditPositionEvent();
\r
4607 SAY("to set up a position type a FEN");
\r
4610 case IDM_Training:
\r
4614 case IDM_ShowGameList:
\r
4615 ShowGameListProc();
\r
4618 case IDM_EditTags:
\r
4622 case IDM_EditComment:
\r
4623 if (commentUp && editComment) {
\r
4626 EditCommentEvent();
\r
4646 case IDM_CallFlag:
\r
4666 case IDM_StopObserving:
\r
4667 StopObservingEvent();
\r
4670 case IDM_StopExamining:
\r
4671 StopExaminingEvent();
\r
4675 UploadGameEvent();
\r
4678 case IDM_TypeInMove:
\r
4679 PopUpMoveDialog('\000');
\r
4682 case IDM_TypeInName:
\r
4683 PopUpNameDialog('\000');
\r
4686 case IDM_Backward:
\r
4688 SetFocus(hwndMain);
\r
4695 SetFocus(hwndMain);
\r
4700 SetFocus(hwndMain);
\r
4705 SetFocus(hwndMain);
\r
4709 RevertEvent(FALSE);
\r
4712 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4713 RevertEvent(TRUE);
\r
4716 case IDM_TruncateGame:
\r
4717 TruncateGameEvent();
\r
4724 case IDM_RetractMove:
\r
4725 RetractMoveEvent();
\r
4728 case IDM_FlipView:
\r
4729 flipView = !flipView;
\r
4730 DrawPosition(FALSE, NULL);
\r
4733 case IDM_FlipClock:
\r
4734 flipClock = !flipClock;
\r
4735 DisplayBothClocks();
\r
4736 DrawPosition(FALSE, NULL);
\r
4739 case IDM_MuteSounds:
\r
4740 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4741 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4742 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4745 case IDM_GeneralOptions:
\r
4746 GeneralOptionsPopup(hwnd);
\r
4747 DrawPosition(TRUE, NULL);
\r
4750 case IDM_BoardOptions:
\r
4751 BoardOptionsPopup(hwnd);
\r
4754 case IDM_EnginePlayOptions:
\r
4755 EnginePlayOptionsPopup(hwnd);
\r
4758 case IDM_Engine1Options:
\r
4759 EngineOptionsPopup(hwnd, &first);
\r
4762 case IDM_Engine2Options:
\r
4763 EngineOptionsPopup(hwnd, &second);
\r
4766 case IDM_OptionsUCI:
\r
4767 UciOptionsPopup(hwnd);
\r
4770 case IDM_IcsOptions:
\r
4771 IcsOptionsPopup(hwnd);
\r
4775 FontsOptionsPopup(hwnd);
\r
4779 SoundOptionsPopup(hwnd);
\r
4782 case IDM_CommPort:
\r
4783 CommPortOptionsPopup(hwnd);
\r
4786 case IDM_LoadOptions:
\r
4787 LoadOptionsPopup(hwnd);
\r
4790 case IDM_SaveOptions:
\r
4791 SaveOptionsPopup(hwnd);
\r
4794 case IDM_TimeControl:
\r
4795 TimeControlOptionsPopup(hwnd);
\r
4798 case IDM_SaveSettings:
\r
4799 SaveSettings(settingsFileName);
\r
4802 case IDM_SaveSettingsOnExit:
\r
4803 saveSettingsOnExit = !saveSettingsOnExit;
\r
4804 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4805 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4806 MF_CHECKED : MF_UNCHECKED));
\r
4817 case IDM_AboutGame:
\r
4822 appData.debugMode = !appData.debugMode;
\r
4823 if (appData.debugMode) {
\r
4824 char dir[MSG_SIZ];
\r
4825 GetCurrentDirectory(MSG_SIZ, dir);
\r
4826 SetCurrentDirectory(installDir);
\r
4827 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4828 SetCurrentDirectory(dir);
\r
4829 setbuf(debugFP, NULL);
\r
4836 case IDM_HELPCONTENTS:
\r
4837 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4838 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4839 MessageBox (GetFocus(),
\r
4840 "Unable to activate help",
\r
4841 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4845 case IDM_HELPSEARCH:
\r
4846 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4847 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4848 MessageBox (GetFocus(),
\r
4849 "Unable to activate help",
\r
4850 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4854 case IDM_HELPHELP:
\r
4855 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4856 MessageBox (GetFocus(),
\r
4857 "Unable to activate help",
\r
4858 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4863 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4865 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4866 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4867 FreeProcInstance(lpProc);
\r
4870 case IDM_DirectCommand1:
\r
4871 AskQuestionEvent("Direct Command",
\r
4872 "Send to chess program:", "", "1");
\r
4874 case IDM_DirectCommand2:
\r
4875 AskQuestionEvent("Direct Command",
\r
4876 "Send to second chess program:", "", "2");
\r
4879 case EP_WhitePawn:
\r
4880 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4881 fromX = fromY = -1;
\r
4884 case EP_WhiteKnight:
\r
4885 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4886 fromX = fromY = -1;
\r
4889 case EP_WhiteBishop:
\r
4890 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4891 fromX = fromY = -1;
\r
4894 case EP_WhiteRook:
\r
4895 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4896 fromX = fromY = -1;
\r
4899 case EP_WhiteQueen:
\r
4900 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4901 fromX = fromY = -1;
\r
4904 case EP_WhiteFerz:
\r
4905 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4906 fromX = fromY = -1;
\r
4909 case EP_WhiteWazir:
\r
4910 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4911 fromX = fromY = -1;
\r
4914 case EP_WhiteAlfil:
\r
4915 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4916 fromX = fromY = -1;
\r
4919 case EP_WhiteCannon:
\r
4920 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4921 fromX = fromY = -1;
\r
4924 case EP_WhiteCardinal:
\r
4925 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4926 fromX = fromY = -1;
\r
4929 case EP_WhiteMarshall:
\r
4930 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4931 fromX = fromY = -1;
\r
4934 case EP_WhiteKing:
\r
4935 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4936 fromX = fromY = -1;
\r
4939 case EP_BlackPawn:
\r
4940 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4941 fromX = fromY = -1;
\r
4944 case EP_BlackKnight:
\r
4945 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4946 fromX = fromY = -1;
\r
4949 case EP_BlackBishop:
\r
4950 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4951 fromX = fromY = -1;
\r
4954 case EP_BlackRook:
\r
4955 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4956 fromX = fromY = -1;
\r
4959 case EP_BlackQueen:
\r
4960 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4961 fromX = fromY = -1;
\r
4964 case EP_BlackFerz:
\r
4965 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4966 fromX = fromY = -1;
\r
4969 case EP_BlackWazir:
\r
4970 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4971 fromX = fromY = -1;
\r
4974 case EP_BlackAlfil:
\r
4975 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4976 fromX = fromY = -1;
\r
4979 case EP_BlackCannon:
\r
4980 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4981 fromX = fromY = -1;
\r
4984 case EP_BlackCardinal:
\r
4985 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4986 fromX = fromY = -1;
\r
4989 case EP_BlackMarshall:
\r
4990 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4991 fromX = fromY = -1;
\r
4994 case EP_BlackKing:
\r
4995 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4996 fromX = fromY = -1;
\r
4999 case EP_EmptySquare:
\r
5000 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5001 fromX = fromY = -1;
\r
5004 case EP_ClearBoard:
\r
5005 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5006 fromX = fromY = -1;
\r
5010 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5011 fromX = fromY = -1;
\r
5015 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5016 fromX = fromY = -1;
\r
5020 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5021 fromX = fromY = -1;
\r
5025 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5026 fromX = fromY = -1;
\r
5030 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5031 fromX = fromY = -1;
\r
5035 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5036 fromX = fromY = -1;
\r
5040 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5041 fromX = fromY = -1;
\r
5045 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5046 fromX = fromY = -1;
\r
5050 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5051 fromX = fromY = -1;
\r
5055 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5061 case CLOCK_TIMER_ID:
\r
5062 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5063 clockTimerEvent = 0;
\r
5064 DecrementClocks(); /* call into back end */
\r
5066 case LOAD_GAME_TIMER_ID:
\r
5067 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5068 loadGameTimerEvent = 0;
\r
5069 AutoPlayGameLoop(); /* call into back end */
\r
5071 case ANALYSIS_TIMER_ID:
\r
5072 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5073 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5074 AnalysisPeriodicEvent(0);
\r
5076 KillTimer(hwnd, analysisTimerEvent);
\r
5077 analysisTimerEvent = 0;
\r
5080 case DELAYED_TIMER_ID:
\r
5081 KillTimer(hwnd, delayedTimerEvent);
\r
5082 delayedTimerEvent = 0;
\r
5083 delayedTimerCallback();
\r
5088 case WM_USER_Input:
\r
5089 InputEvent(hwnd, message, wParam, lParam);
\r
5092 /* [AS] Also move "attached" child windows */
\r
5093 case WM_WINDOWPOSCHANGING:
\r
5095 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5096 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5098 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5099 /* Window is moving */
\r
5102 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5103 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5104 rcMain.right = wpMain.x + wpMain.width;
\r
5105 rcMain.top = wpMain.y;
\r
5106 rcMain.bottom = wpMain.y + wpMain.height;
\r
5108 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5109 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5110 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5111 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5112 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5113 wpMain.x = lpwp->x;
\r
5114 wpMain.y = lpwp->y;
\r
5119 /* [AS] Snapping */
\r
5120 case WM_ENTERSIZEMOVE:
\r
5121 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5122 if (hwnd == hwndMain) {
\r
5123 doingSizing = TRUE;
\r
5126 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5130 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5131 if (hwnd == hwndMain) {
\r
5132 lastSizing = wParam;
\r
5137 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5138 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5140 case WM_EXITSIZEMOVE:
\r
5141 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5142 if (hwnd == hwndMain) {
\r
5144 doingSizing = FALSE;
\r
5145 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5146 GetClientRect(hwnd, &client);
\r
5147 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5149 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5151 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5154 case WM_DESTROY: /* message: window being destroyed */
\r
5155 PostQuitMessage(0);
\r
5159 if (hwnd == hwndMain) {
\r
5164 default: /* Passes it on if unprocessed */
\r
5165 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5170 /*---------------------------------------------------------------------------*\
\r
5172 * Misc utility routines
\r
5174 \*---------------------------------------------------------------------------*/
\r
5177 * Decent random number generator, at least not as bad as Windows
\r
5178 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5180 unsigned int randstate;
\r
5185 randstate = randstate * 1664525 + 1013904223;
\r
5186 return (int) randstate & 0x7fffffff;
\r
5190 mysrandom(unsigned int seed)
\r
5197 * returns TRUE if user selects a different color, FALSE otherwise
\r
5201 ChangeColor(HWND hwnd, COLORREF *which)
\r
5203 static BOOL firstTime = TRUE;
\r
5204 static DWORD customColors[16];
\r
5206 COLORREF newcolor;
\r
5211 /* Make initial colors in use available as custom colors */
\r
5212 /* Should we put the compiled-in defaults here instead? */
\r
5214 customColors[i++] = lightSquareColor & 0xffffff;
\r
5215 customColors[i++] = darkSquareColor & 0xffffff;
\r
5216 customColors[i++] = whitePieceColor & 0xffffff;
\r
5217 customColors[i++] = blackPieceColor & 0xffffff;
\r
5218 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5219 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5221 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5222 customColors[i++] = textAttribs[ccl].color;
\r
5224 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5225 firstTime = FALSE;
\r
5228 cc.lStructSize = sizeof(cc);
\r
5229 cc.hwndOwner = hwnd;
\r
5230 cc.hInstance = NULL;
\r
5231 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5232 cc.lpCustColors = (LPDWORD) customColors;
\r
5233 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5235 if (!ChooseColor(&cc)) return FALSE;
\r
5237 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5238 if (newcolor == *which) return FALSE;
\r
5239 *which = newcolor;
\r
5243 InitDrawingColors();
\r
5244 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5249 MyLoadSound(MySound *ms)
\r
5255 if (ms->data) free(ms->data);
\r
5258 switch (ms->name[0]) {
\r
5264 /* System sound from Control Panel. Don't preload here. */
\r
5268 if (ms->name[1] == NULLCHAR) {
\r
5269 /* "!" alone = silence */
\r
5272 /* Builtin wave resource. Error if not found. */
\r
5273 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5274 if (h == NULL) break;
\r
5275 ms->data = (void *)LoadResource(hInst, h);
\r
5276 if (h == NULL) break;
\r
5281 /* .wav file. Error if not found. */
\r
5282 f = fopen(ms->name, "rb");
\r
5283 if (f == NULL) break;
\r
5284 if (fstat(fileno(f), &st) < 0) break;
\r
5285 ms->data = malloc(st.st_size);
\r
5286 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5292 char buf[MSG_SIZ];
\r
5293 sprintf(buf, "Error loading sound %s", ms->name);
\r
5294 DisplayError(buf, GetLastError());
\r
5300 MyPlaySound(MySound *ms)
\r
5302 BOOLEAN ok = FALSE;
\r
5304 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5305 switch (ms->name[0]) {
\r
5307 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5312 /* System sound from Control Panel (deprecated feature).
\r
5313 "$" alone or an unset sound name gets default beep (still in use). */
\r
5314 if (ms->name[1]) {
\r
5315 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5317 if (!ok) ok = MessageBeep(MB_OK);
\r
5320 /* Builtin wave resource, or "!" alone for silence */
\r
5321 if (ms->name[1]) {
\r
5322 if (ms->data == NULL) return FALSE;
\r
5323 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5329 /* .wav file. Error if not found. */
\r
5330 if (ms->data == NULL) return FALSE;
\r
5331 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5334 /* Don't print an error: this can happen innocently if the sound driver
\r
5335 is busy; for instance, if another instance of WinBoard is playing
\r
5336 a sound at about the same time. */
\r
5342 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5345 OPENFILENAME *ofn;
\r
5346 static UINT *number; /* gross that this is static */
\r
5348 switch (message) {
\r
5349 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5350 /* Center the dialog over the application window */
\r
5351 ofn = (OPENFILENAME *) lParam;
\r
5352 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5353 number = (UINT *) ofn->lCustData;
\r
5354 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5358 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5359 return FALSE; /* Allow for further processing */
\r
5362 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5363 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5365 return FALSE; /* Allow for further processing */
\r
5371 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5373 static UINT *number;
\r
5374 OPENFILENAME *ofname;
\r
5377 case WM_INITDIALOG:
\r
5378 ofname = (OPENFILENAME *)lParam;
\r
5379 number = (UINT *)(ofname->lCustData);
\r
5382 ofnot = (OFNOTIFY *)lParam;
\r
5383 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5384 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5393 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5394 char *nameFilt, char *dlgTitle, UINT *number,
\r
5395 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5397 OPENFILENAME openFileName;
\r
5398 char buf1[MSG_SIZ];
\r
5401 if (fileName == NULL) fileName = buf1;
\r
5402 if (defName == NULL) {
\r
5403 strcpy(fileName, "*.");
\r
5404 strcat(fileName, defExt);
\r
5406 strcpy(fileName, defName);
\r
5408 if (fileTitle) strcpy(fileTitle, "");
\r
5409 if (number) *number = 0;
\r
5411 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5412 openFileName.hwndOwner = hwnd;
\r
5413 openFileName.hInstance = (HANDLE) hInst;
\r
5414 openFileName.lpstrFilter = nameFilt;
\r
5415 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5416 openFileName.nMaxCustFilter = 0L;
\r
5417 openFileName.nFilterIndex = 1L;
\r
5418 openFileName.lpstrFile = fileName;
\r
5419 openFileName.nMaxFile = MSG_SIZ;
\r
5420 openFileName.lpstrFileTitle = fileTitle;
\r
5421 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5422 openFileName.lpstrInitialDir = NULL;
\r
5423 openFileName.lpstrTitle = dlgTitle;
\r
5424 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5425 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5426 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5427 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5428 openFileName.nFileOffset = 0;
\r
5429 openFileName.nFileExtension = 0;
\r
5430 openFileName.lpstrDefExt = defExt;
\r
5431 openFileName.lCustData = (LONG) number;
\r
5432 openFileName.lpfnHook = oldDialog ?
\r
5433 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5434 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5436 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5437 GetOpenFileName(&openFileName)) {
\r
5438 /* open the file */
\r
5439 f = fopen(openFileName.lpstrFile, write);
\r
5441 MessageBox(hwnd, "File open failed", NULL,
\r
5442 MB_OK|MB_ICONEXCLAMATION);
\r
5446 int err = CommDlgExtendedError();
\r
5447 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5456 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5458 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5461 * Get the first pop-up menu in the menu template. This is the
\r
5462 * menu that TrackPopupMenu displays.
\r
5464 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5466 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5469 * TrackPopup uses screen coordinates, so convert the
\r
5470 * coordinates of the mouse click to screen coordinates.
\r
5472 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5474 /* Draw and track the floating pop-up menu. */
\r
5475 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5476 pt.x, pt.y, 0, hwnd, NULL);
\r
5478 /* Destroy the menu.*/
\r
5479 DestroyMenu(hmenu);
\r
5484 int sizeX, sizeY, newSizeX, newSizeY;
\r
5486 } ResizeEditPlusButtonsClosure;
\r
5489 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5491 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5495 if (hChild == cl->hText) return TRUE;
\r
5496 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5497 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5498 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5499 ScreenToClient(cl->hDlg, &pt);
\r
5500 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5501 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5505 /* Resize a dialog that has a (rich) edit field filling most of
\r
5506 the top, with a row of buttons below */
\r
5508 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5511 int newTextHeight, newTextWidth;
\r
5512 ResizeEditPlusButtonsClosure cl;
\r
5514 /*if (IsIconic(hDlg)) return;*/
\r
5515 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5517 cl.hdwp = BeginDeferWindowPos(8);
\r
5519 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5520 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5521 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5522 if (newTextHeight < 0) {
\r
5523 newSizeY += -newTextHeight;
\r
5524 newTextHeight = 0;
\r
5526 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5527 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5533 cl.newSizeX = newSizeX;
\r
5534 cl.newSizeY = newSizeY;
\r
5535 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5537 EndDeferWindowPos(cl.hdwp);
\r
5540 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5542 RECT rChild, rParent;
\r
5543 int wChild, hChild, wParent, hParent;
\r
5544 int wScreen, hScreen, xNew, yNew;
\r
5547 /* Get the Height and Width of the child window */
\r
5548 GetWindowRect (hwndChild, &rChild);
\r
5549 wChild = rChild.right - rChild.left;
\r
5550 hChild = rChild.bottom - rChild.top;
\r
5552 /* Get the Height and Width of the parent window */
\r
5553 GetWindowRect (hwndParent, &rParent);
\r
5554 wParent = rParent.right - rParent.left;
\r
5555 hParent = rParent.bottom - rParent.top;
\r
5557 /* Get the display limits */
\r
5558 hdc = GetDC (hwndChild);
\r
5559 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5560 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5561 ReleaseDC(hwndChild, hdc);
\r
5563 /* Calculate new X position, then adjust for screen */
\r
5564 xNew = rParent.left + ((wParent - wChild) /2);
\r
5567 } else if ((xNew+wChild) > wScreen) {
\r
5568 xNew = wScreen - wChild;
\r
5571 /* Calculate new Y position, then adjust for screen */
\r
5573 yNew = rParent.top + ((hParent - hChild) /2);
\r
5576 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5581 } else if ((yNew+hChild) > hScreen) {
\r
5582 yNew = hScreen - hChild;
\r
5585 /* Set it, and return */
\r
5586 return SetWindowPos (hwndChild, NULL,
\r
5587 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5590 /* Center one window over another */
\r
5591 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5593 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5596 /*---------------------------------------------------------------------------*\
\r
5598 * Startup Dialog functions
\r
5600 \*---------------------------------------------------------------------------*/
\r
5602 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5604 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5606 while (*cd != NULL) {
\r
5607 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5613 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5615 char buf1[MAX_ARG_LEN];
\r
5618 if (str[0] == '@') {
\r
5619 FILE* f = fopen(str + 1, "r");
\r
5621 DisplayFatalError(str + 1, errno, 2);
\r
5624 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5626 buf1[len] = NULLCHAR;
\r
5630 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5633 char buf[MSG_SIZ];
\r
5634 char *end = strchr(str, '\n');
\r
5635 if (end == NULL) return;
\r
5636 memcpy(buf, str, end - str);
\r
5637 buf[end - str] = NULLCHAR;
\r
5638 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5644 SetStartupDialogEnables(HWND hDlg)
\r
5646 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5647 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5648 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5649 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5650 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5651 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5652 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5653 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5654 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5655 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5656 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5657 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5658 IsDlgButtonChecked(hDlg, OPT_View));
\r
5662 QuoteForFilename(char *filename)
\r
5664 int dquote, space;
\r
5665 dquote = strchr(filename, '"') != NULL;
\r
5666 space = strchr(filename, ' ') != NULL;
\r
5667 if (dquote || space) {
\r
5679 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5681 char buf[MSG_SIZ];
\r
5684 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5685 q = QuoteForFilename(nthcp);
\r
5686 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5687 if (*nthdir != NULLCHAR) {
\r
5688 q = QuoteForFilename(nthdir);
\r
5689 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5691 if (*nthcp == NULLCHAR) {
\r
5692 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5693 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5694 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5695 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5700 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5702 char buf[MSG_SIZ];
\r
5706 switch (message) {
\r
5707 case WM_INITDIALOG:
\r
5708 /* Center the dialog */
\r
5709 CenterWindow (hDlg, GetDesktopWindow());
\r
5710 /* Initialize the dialog items */
\r
5711 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5712 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5713 firstChessProgramNames);
\r
5714 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5715 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5716 secondChessProgramNames);
\r
5717 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5718 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5719 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5720 if (*appData.icsHelper != NULLCHAR) {
\r
5721 char *q = QuoteForFilename(appData.icsHelper);
\r
5722 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5724 if (*appData.icsHost == NULLCHAR) {
\r
5725 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5726 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5727 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5728 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5729 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5732 if (appData.icsActive) {
\r
5733 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5735 else if (appData.noChessProgram) {
\r
5736 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5739 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5742 SetStartupDialogEnables(hDlg);
\r
5746 switch (LOWORD(wParam)) {
\r
5748 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5749 strcpy(buf, "/fcp=");
\r
5750 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5752 ParseArgs(StringGet, &p);
\r
5753 strcpy(buf, "/scp=");
\r
5754 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5756 ParseArgs(StringGet, &p);
\r
5757 appData.noChessProgram = FALSE;
\r
5758 appData.icsActive = FALSE;
\r
5759 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5760 strcpy(buf, "/ics /icshost=");
\r
5761 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5763 ParseArgs(StringGet, &p);
\r
5764 if (appData.zippyPlay) {
\r
5765 strcpy(buf, "/fcp=");
\r
5766 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5768 ParseArgs(StringGet, &p);
\r
5770 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5771 appData.noChessProgram = TRUE;
\r
5772 appData.icsActive = FALSE;
\r
5774 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5775 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5778 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5779 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5781 ParseArgs(StringGet, &p);
\r
5783 EndDialog(hDlg, TRUE);
\r
5790 case IDM_HELPCONTENTS:
\r
5791 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5792 MessageBox (GetFocus(),
\r
5793 "Unable to activate help",
\r
5794 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5799 SetStartupDialogEnables(hDlg);
\r
5807 /*---------------------------------------------------------------------------*\
\r
5809 * About box dialog functions
\r
5811 \*---------------------------------------------------------------------------*/
\r
5813 /* Process messages for "About" dialog box */
\r
5815 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5817 switch (message) {
\r
5818 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5819 /* Center the dialog over the application window */
\r
5820 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5821 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5825 case WM_COMMAND: /* message: received a command */
\r
5826 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5827 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5828 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5836 /*---------------------------------------------------------------------------*\
\r
5838 * Comment Dialog functions
\r
5840 \*---------------------------------------------------------------------------*/
\r
5843 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5845 static HANDLE hwndText = NULL;
\r
5846 int len, newSizeX, newSizeY, flags;
\r
5847 static int sizeX, sizeY;
\r
5852 switch (message) {
\r
5853 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5854 /* Initialize the dialog items */
\r
5855 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5856 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5857 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5858 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5859 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5860 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5861 SetWindowText(hDlg, commentTitle);
\r
5862 if (editComment) {
\r
5863 SetFocus(hwndText);
\r
5865 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5867 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5868 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5869 MAKELPARAM(FALSE, 0));
\r
5870 /* Size and position the dialog */
\r
5871 if (!commentDialog) {
\r
5872 commentDialog = hDlg;
\r
5873 flags = SWP_NOZORDER;
\r
5874 GetClientRect(hDlg, &rect);
\r
5875 sizeX = rect.right;
\r
5876 sizeY = rect.bottom;
\r
5877 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5878 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5879 WINDOWPLACEMENT wp;
\r
5880 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5881 wp.length = sizeof(WINDOWPLACEMENT);
\r
5883 wp.showCmd = SW_SHOW;
\r
5884 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5885 wp.rcNormalPosition.left = wpComment.x;
\r
5886 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5887 wp.rcNormalPosition.top = wpComment.y;
\r
5888 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5889 SetWindowPlacement(hDlg, &wp);
\r
5891 GetClientRect(hDlg, &rect);
\r
5892 newSizeX = rect.right;
\r
5893 newSizeY = rect.bottom;
\r
5894 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5895 newSizeX, newSizeY);
\r
5900 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5903 case WM_COMMAND: /* message: received a command */
\r
5904 switch (LOWORD(wParam)) {
\r
5906 if (editComment) {
\r
5908 /* Read changed options from the dialog box */
\r
5909 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5910 len = GetWindowTextLength(hwndText);
\r
5911 str = (char *) malloc(len + 1);
\r
5912 GetWindowText(hwndText, str, len + 1);
\r
5921 ReplaceComment(commentIndex, str);
\r
5928 case OPT_CancelComment:
\r
5932 case OPT_ClearComment:
\r
5933 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5936 case OPT_EditComment:
\r
5937 EditCommentEvent();
\r
5945 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5946 if( wParam == OPT_CommentText ) {
\r
5947 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5949 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5953 pt.x = LOWORD( lpMF->lParam );
\r
5954 pt.y = HIWORD( lpMF->lParam );
\r
5956 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5958 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5959 len = GetWindowTextLength(hwndText);
\r
5960 str = (char *) malloc(len + 1);
\r
5961 GetWindowText(hwndText, str, len + 1);
\r
5962 ReplaceComment(commentIndex, str);
\r
5963 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5964 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5967 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5968 lpMF->msg = WM_USER;
\r
5976 newSizeX = LOWORD(lParam);
\r
5977 newSizeY = HIWORD(lParam);
\r
5978 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5983 case WM_GETMINMAXINFO:
\r
5984 /* Prevent resizing window too small */
\r
5985 mmi = (MINMAXINFO *) lParam;
\r
5986 mmi->ptMinTrackSize.x = 100;
\r
5987 mmi->ptMinTrackSize.y = 100;
\r
5994 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5999 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6001 if (str == NULL) str = "";
\r
6002 p = (char *) malloc(2 * strlen(str) + 2);
\r
6005 if (*str == '\n') *q++ = '\r';
\r
6009 if (commentText != NULL) free(commentText);
\r
6011 commentIndex = index;
\r
6012 commentTitle = title;
\r
6014 editComment = edit;
\r
6016 if (commentDialog) {
\r
6017 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6018 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6020 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6021 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6022 hwndMain, (DLGPROC)lpProc);
\r
6023 FreeProcInstance(lpProc);
\r
6029 /*---------------------------------------------------------------------------*\
\r
6031 * Type-in move dialog functions
\r
6033 \*---------------------------------------------------------------------------*/
\r
6036 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6038 char move[MSG_SIZ];
\r
6040 ChessMove moveType;
\r
6041 int fromX, fromY, toX, toY;
\r
6044 switch (message) {
\r
6045 case WM_INITDIALOG:
\r
6046 move[0] = (char) lParam;
\r
6047 move[1] = NULLCHAR;
\r
6048 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6049 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6050 SetWindowText(hInput, move);
\r
6052 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6056 switch (LOWORD(wParam)) {
\r
6058 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6059 { int n; Board board;
\r
6061 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6062 EditPositionPasteFEN(move);
\r
6063 EndDialog(hDlg, TRUE);
\r
6066 // [HGM] movenum: allow move number to be typed in any mode
\r
6067 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6069 EndDialog(hDlg, TRUE);
\r
6073 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6074 gameMode != Training) {
\r
6075 DisplayMoveError("Displayed move is not current");
\r
6077 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6078 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6079 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6080 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6081 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6082 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6083 if (gameMode != Training)
\r
6084 forwardMostMove = currentMove;
\r
6085 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6087 DisplayMoveError("Could not parse move");
\r
6090 EndDialog(hDlg, TRUE);
\r
6093 EndDialog(hDlg, FALSE);
\r
6104 PopUpMoveDialog(char firstchar)
\r
6108 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6109 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6110 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6111 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6112 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6113 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6114 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6115 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6116 gameMode == Training) {
\r
6117 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6118 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6119 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6120 FreeProcInstance(lpProc);
\r
6124 /*---------------------------------------------------------------------------*\
\r
6126 * Type-in name dialog functions
\r
6128 \*---------------------------------------------------------------------------*/
\r
6131 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6133 char move[MSG_SIZ];
\r
6136 switch (message) {
\r
6137 case WM_INITDIALOG:
\r
6138 move[0] = (char) lParam;
\r
6139 move[1] = NULLCHAR;
\r
6140 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6141 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6142 SetWindowText(hInput, move);
\r
6144 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6148 switch (LOWORD(wParam)) {
\r
6150 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6151 appData.userName = strdup(move);
\r
6154 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6155 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6156 DisplayTitle(move);
\r
6160 EndDialog(hDlg, TRUE);
\r
6163 EndDialog(hDlg, FALSE);
\r
6174 PopUpNameDialog(char firstchar)
\r
6178 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6179 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6180 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6181 FreeProcInstance(lpProc);
\r
6184 /*---------------------------------------------------------------------------*\
\r
6188 \*---------------------------------------------------------------------------*/
\r
6190 /* Nonmodal error box */
\r
6191 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6192 WPARAM wParam, LPARAM lParam);
\r
6195 ErrorPopUp(char *title, char *content)
\r
6199 BOOLEAN modal = hwndMain == NULL;
\r
6217 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6218 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6221 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6223 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6224 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6225 hwndMain, (DLGPROC)lpProc);
\r
6226 FreeProcInstance(lpProc);
\r
6233 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6234 if (errorDialog == NULL) return;
\r
6235 DestroyWindow(errorDialog);
\r
6236 errorDialog = NULL;
\r
6237 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6241 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6246 switch (message) {
\r
6247 case WM_INITDIALOG:
\r
6248 GetWindowRect(hDlg, &rChild);
\r
6251 SetWindowPos(hDlg, NULL, rChild.left,
\r
6252 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6253 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6257 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6258 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6259 and it doesn't work when you resize the dialog.
\r
6260 For now, just give it a default position.
\r
6262 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6264 errorDialog = hDlg;
\r
6265 SetWindowText(hDlg, errorTitle);
\r
6266 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6267 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6271 switch (LOWORD(wParam)) {
\r
6274 if (errorDialog == hDlg) errorDialog = NULL;
\r
6275 DestroyWindow(hDlg);
\r
6287 HWND gothicDialog = NULL;
\r
6290 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6294 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6296 switch (message) {
\r
6297 case WM_INITDIALOG:
\r
6298 GetWindowRect(hDlg, &rChild);
\r
6300 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6304 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6305 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6306 and it doesn't work when you resize the dialog.
\r
6307 For now, just give it a default position.
\r
6309 gothicDialog = hDlg;
\r
6310 SetWindowText(hDlg, errorTitle);
\r
6311 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6312 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6316 switch (LOWORD(wParam)) {
\r
6319 if (errorDialog == hDlg) errorDialog = NULL;
\r
6320 DestroyWindow(hDlg);
\r
6332 GothicPopUp(char *title, VariantClass variant)
\r
6335 static char *lastTitle;
\r
6337 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6338 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6340 if(lastTitle != title && gothicDialog != NULL) {
\r
6341 DestroyWindow(gothicDialog);
\r
6342 gothicDialog = NULL;
\r
6344 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6345 title = lastTitle;
\r
6346 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6347 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6348 hwndMain, (DLGPROC)lpProc);
\r
6349 FreeProcInstance(lpProc);
\r
6354 /*---------------------------------------------------------------------------*\
\r
6356 * Ics Interaction console functions
\r
6358 \*---------------------------------------------------------------------------*/
\r
6360 #define HISTORY_SIZE 64
\r
6361 static char *history[HISTORY_SIZE];
\r
6362 int histIn = 0, histP = 0;
\r
6365 SaveInHistory(char *cmd)
\r
6367 if (history[histIn] != NULL) {
\r
6368 free(history[histIn]);
\r
6369 history[histIn] = NULL;
\r
6371 if (*cmd == NULLCHAR) return;
\r
6372 history[histIn] = StrSave(cmd);
\r
6373 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6374 if (history[histIn] != NULL) {
\r
6375 free(history[histIn]);
\r
6376 history[histIn] = NULL;
\r
6382 PrevInHistory(char *cmd)
\r
6385 if (histP == histIn) {
\r
6386 if (history[histIn] != NULL) free(history[histIn]);
\r
6387 history[histIn] = StrSave(cmd);
\r
6389 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6390 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6392 return history[histP];
\r
6398 if (histP == histIn) return NULL;
\r
6399 histP = (histP + 1) % HISTORY_SIZE;
\r
6400 return history[histP];
\r
6404 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6408 hmenu = LoadMenu(hInst, "TextMenu");
\r
6409 h = GetSubMenu(hmenu, 0);
\r
6411 if (strcmp(e->item, "-") == 0) {
\r
6412 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6413 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6414 int flags = MF_STRING, j = 0;
\r
6415 if (e->item[0] == '|') {
\r
6416 flags |= MF_MENUBARBREAK;
\r
6419 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6420 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6428 WNDPROC consoleTextWindowProc;
\r
6431 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6433 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6434 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6438 SetWindowText(hInput, command);
\r
6440 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6442 sel.cpMin = 999999;
\r
6443 sel.cpMax = 999999;
\r
6444 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6449 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6450 if (sel.cpMin == sel.cpMax) {
\r
6451 /* Expand to surrounding word */
\r
6454 tr.chrg.cpMax = sel.cpMin;
\r
6455 tr.chrg.cpMin = --sel.cpMin;
\r
6456 if (sel.cpMin < 0) break;
\r
6457 tr.lpstrText = name;
\r
6458 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6459 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6463 tr.chrg.cpMin = sel.cpMax;
\r
6464 tr.chrg.cpMax = ++sel.cpMax;
\r
6465 tr.lpstrText = name;
\r
6466 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6467 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6470 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6471 MessageBeep(MB_ICONEXCLAMATION);
\r
6475 tr.lpstrText = name;
\r
6476 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6478 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6479 MessageBeep(MB_ICONEXCLAMATION);
\r
6482 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6485 sprintf(buf, "%s %s", command, name);
\r
6486 SetWindowText(hInput, buf);
\r
6487 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6489 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6490 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6491 SetWindowText(hInput, buf);
\r
6492 sel.cpMin = 999999;
\r
6493 sel.cpMax = 999999;
\r
6494 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6500 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6505 switch (message) {
\r
6507 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6510 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6513 sel.cpMin = 999999;
\r
6514 sel.cpMax = 999999;
\r
6515 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6516 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6521 if(wParam != '\022') {
\r
6522 if (wParam == '\t') {
\r
6523 if (GetKeyState(VK_SHIFT) < 0) {
\r
6525 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6526 if (buttonDesc[0].hwnd) {
\r
6527 SetFocus(buttonDesc[0].hwnd);
\r
6529 SetFocus(hwndMain);
\r
6533 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6536 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6537 JAWS_DELETE( SetFocus(hInput); )
\r
6538 SendMessage(hInput, message, wParam, lParam);
\r
6541 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6542 case WM_RBUTTONDOWN:
\r
6543 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6544 /* Move selection here if it was empty */
\r
6546 pt.x = LOWORD(lParam);
\r
6547 pt.y = HIWORD(lParam);
\r
6548 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6549 if (sel.cpMin == sel.cpMax) {
\r
6550 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6551 sel.cpMax = sel.cpMin;
\r
6552 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6554 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6555 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6557 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6558 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6559 if (sel.cpMin == sel.cpMax) {
\r
6560 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6561 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6563 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6564 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6566 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6567 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6568 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6569 MenuPopup(hwnd, pt, hmenu, -1);
\r
6573 case WM_RBUTTONUP:
\r
6574 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6575 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6576 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6580 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6582 return SendMessage(hInput, message, wParam, lParam);
\r
6583 case WM_MBUTTONDOWN:
\r
6584 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6586 switch (LOWORD(wParam)) {
\r
6587 case IDM_QuickPaste:
\r
6589 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6590 if (sel.cpMin == sel.cpMax) {
\r
6591 MessageBeep(MB_ICONEXCLAMATION);
\r
6594 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6595 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6596 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6601 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6604 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6607 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6611 int i = LOWORD(wParam) - IDM_CommandX;
\r
6612 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6613 icsTextMenuEntry[i].command != NULL) {
\r
6614 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6615 icsTextMenuEntry[i].getname,
\r
6616 icsTextMenuEntry[i].immediate);
\r
6624 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6627 WNDPROC consoleInputWindowProc;
\r
6630 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6632 char buf[MSG_SIZ];
\r
6634 static BOOL sendNextChar = FALSE;
\r
6635 static BOOL quoteNextChar = FALSE;
\r
6636 InputSource *is = consoleInputSource;
\r
6640 switch (message) {
\r
6642 if (!appData.localLineEditing || sendNextChar) {
\r
6643 is->buf[0] = (CHAR) wParam;
\r
6645 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6646 sendNextChar = FALSE;
\r
6649 if (quoteNextChar) {
\r
6650 buf[0] = (char) wParam;
\r
6651 buf[1] = NULLCHAR;
\r
6652 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6653 quoteNextChar = FALSE;
\r
6657 case '\r': /* Enter key */
\r
6658 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6659 if (consoleEcho) SaveInHistory(is->buf);
\r
6660 is->buf[is->count++] = '\n';
\r
6661 is->buf[is->count] = NULLCHAR;
\r
6662 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6663 if (consoleEcho) {
\r
6664 ConsoleOutput(is->buf, is->count, TRUE);
\r
6665 } else if (appData.localLineEditing) {
\r
6666 ConsoleOutput("\n", 1, TRUE);
\r
6669 case '\033': /* Escape key */
\r
6670 SetWindowText(hwnd, "");
\r
6671 cf.cbSize = sizeof(CHARFORMAT);
\r
6672 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6673 if (consoleEcho) {
\r
6674 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6676 cf.crTextColor = COLOR_ECHOOFF;
\r
6678 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6679 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6681 case '\t': /* Tab key */
\r
6682 if (GetKeyState(VK_SHIFT) < 0) {
\r
6684 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6687 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6688 if (buttonDesc[0].hwnd) {
\r
6689 SetFocus(buttonDesc[0].hwnd);
\r
6691 SetFocus(hwndMain);
\r
6695 case '\023': /* Ctrl+S */
\r
6696 sendNextChar = TRUE;
\r
6698 case '\021': /* Ctrl+Q */
\r
6699 quoteNextChar = TRUE;
\r
6709 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6710 p = PrevInHistory(buf);
\r
6712 SetWindowText(hwnd, p);
\r
6713 sel.cpMin = 999999;
\r
6714 sel.cpMax = 999999;
\r
6715 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6720 p = NextInHistory();
\r
6722 SetWindowText(hwnd, p);
\r
6723 sel.cpMin = 999999;
\r
6724 sel.cpMax = 999999;
\r
6725 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6731 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6735 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6739 case WM_MBUTTONDOWN:
\r
6740 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6741 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6743 case WM_RBUTTONUP:
\r
6744 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6745 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6746 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6750 hmenu = LoadMenu(hInst, "InputMenu");
\r
6751 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6752 if (sel.cpMin == sel.cpMax) {
\r
6753 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6754 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6756 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6757 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6759 pt.x = LOWORD(lParam);
\r
6760 pt.y = HIWORD(lParam);
\r
6761 MenuPopup(hwnd, pt, hmenu, -1);
\r
6765 switch (LOWORD(wParam)) {
\r
6767 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6769 case IDM_SelectAll:
\r
6771 sel.cpMax = -1; /*999999?*/
\r
6772 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6775 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6778 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6781 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6786 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6789 #define CO_MAX 100000
\r
6790 #define CO_TRIM 1000
\r
6793 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6795 static SnapData sd;
\r
6796 HWND hText, hInput;
\r
6798 static int sizeX, sizeY;
\r
6799 int newSizeX, newSizeY;
\r
6803 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6804 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6806 switch (message) {
\r
6808 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6810 ENLINK *pLink = (ENLINK*)lParam;
\r
6811 if (pLink->msg == WM_LBUTTONUP)
\r
6815 tr.chrg = pLink->chrg;
\r
6816 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6817 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6818 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6819 free(tr.lpstrText);
\r
6823 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6824 hwndConsole = hDlg;
\r
6826 consoleTextWindowProc = (WNDPROC)
\r
6827 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6828 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6829 consoleInputWindowProc = (WNDPROC)
\r
6830 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6831 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6832 Colorize(ColorNormal, TRUE);
\r
6833 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6834 ChangedConsoleFont();
\r
6835 GetClientRect(hDlg, &rect);
\r
6836 sizeX = rect.right;
\r
6837 sizeY = rect.bottom;
\r
6838 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6839 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6840 WINDOWPLACEMENT wp;
\r
6841 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6842 wp.length = sizeof(WINDOWPLACEMENT);
\r
6844 wp.showCmd = SW_SHOW;
\r
6845 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6846 wp.rcNormalPosition.left = wpConsole.x;
\r
6847 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6848 wp.rcNormalPosition.top = wpConsole.y;
\r
6849 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6850 SetWindowPlacement(hDlg, &wp);
\r
6853 // [HGM] Chessknight's change 2004-07-13
\r
6854 else { /* Determine Defaults */
\r
6855 WINDOWPLACEMENT wp;
\r
6856 wpConsole.x = wpMain.width + 1;
\r
6857 wpConsole.y = wpMain.y;
\r
6858 wpConsole.width = screenWidth - wpMain.width;
\r
6859 wpConsole.height = wpMain.height;
\r
6860 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6861 wp.length = sizeof(WINDOWPLACEMENT);
\r
6863 wp.showCmd = SW_SHOW;
\r
6864 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6865 wp.rcNormalPosition.left = wpConsole.x;
\r
6866 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6867 wp.rcNormalPosition.top = wpConsole.y;
\r
6868 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6869 SetWindowPlacement(hDlg, &wp);
\r
6872 // Allow hText to highlight URLs and send notifications on them
\r
6873 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6874 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6875 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6876 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6890 if (IsIconic(hDlg)) break;
\r
6891 newSizeX = LOWORD(lParam);
\r
6892 newSizeY = HIWORD(lParam);
\r
6893 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6894 RECT rectText, rectInput;
\r
6896 int newTextHeight, newTextWidth;
\r
6897 GetWindowRect(hText, &rectText);
\r
6898 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6899 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6900 if (newTextHeight < 0) {
\r
6901 newSizeY += -newTextHeight;
\r
6902 newTextHeight = 0;
\r
6904 SetWindowPos(hText, NULL, 0, 0,
\r
6905 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6906 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6907 pt.x = rectInput.left;
\r
6908 pt.y = rectInput.top + newSizeY - sizeY;
\r
6909 ScreenToClient(hDlg, &pt);
\r
6910 SetWindowPos(hInput, NULL,
\r
6911 pt.x, pt.y, /* needs client coords */
\r
6912 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6913 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6919 case WM_GETMINMAXINFO:
\r
6920 /* Prevent resizing window too small */
\r
6921 mmi = (MINMAXINFO *) lParam;
\r
6922 mmi->ptMinTrackSize.x = 100;
\r
6923 mmi->ptMinTrackSize.y = 100;
\r
6926 /* [AS] Snapping */
\r
6927 case WM_ENTERSIZEMOVE:
\r
6928 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6931 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6934 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6936 case WM_EXITSIZEMOVE:
\r
6937 UpdateICSWidth(hText);
\r
6938 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6941 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6949 if (hwndConsole) return;
\r
6950 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6951 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6956 ConsoleOutput(char* data, int length, int forceVisible)
\r
6961 char buf[CO_MAX+1];
\r
6964 static int delayLF = 0;
\r
6965 CHARRANGE savesel, sel;
\r
6967 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6975 while (length--) {
\r
6983 } else if (*p == '\007') {
\r
6984 MyPlaySound(&sounds[(int)SoundBell]);
\r
6991 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6992 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6993 /* Save current selection */
\r
6994 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6995 exlen = GetWindowTextLength(hText);
\r
6996 /* Find out whether current end of text is visible */
\r
6997 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6998 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6999 /* Trim existing text if it's too long */
\r
7000 if (exlen + (q - buf) > CO_MAX) {
\r
7001 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7004 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7005 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7007 savesel.cpMin -= trim;
\r
7008 savesel.cpMax -= trim;
\r
7009 if (exlen < 0) exlen = 0;
\r
7010 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7011 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7013 /* Append the new text */
\r
7014 sel.cpMin = exlen;
\r
7015 sel.cpMax = exlen;
\r
7016 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7017 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7018 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7019 if (forceVisible || exlen == 0 ||
\r
7020 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7021 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7022 /* Scroll to make new end of text visible if old end of text
\r
7023 was visible or new text is an echo of user typein */
\r
7024 sel.cpMin = 9999999;
\r
7025 sel.cpMax = 9999999;
\r
7026 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7027 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7028 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7029 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7031 if (savesel.cpMax == exlen || forceVisible) {
\r
7032 /* Move insert point to new end of text if it was at the old
\r
7033 end of text or if the new text is an echo of user typein */
\r
7034 sel.cpMin = 9999999;
\r
7035 sel.cpMax = 9999999;
\r
7036 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7038 /* Restore previous selection */
\r
7039 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7041 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7048 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7052 COLORREF oldFg, oldBg;
\r
7056 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7058 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7059 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7060 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7063 rect.right = x + squareSize;
\r
7065 rect.bottom = y + squareSize;
\r
7068 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7069 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7070 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7071 &rect, str, strlen(str), NULL);
\r
7073 (void) SetTextColor(hdc, oldFg);
\r
7074 (void) SetBkColor(hdc, oldBg);
\r
7075 (void) SelectObject(hdc, oldFont);
\r
7079 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7080 RECT *rect, char *color, char *flagFell)
\r
7084 COLORREF oldFg, oldBg;
\r
7087 if (appData.clockMode) {
\r
7089 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7091 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7098 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7099 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7101 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7102 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7104 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7108 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7109 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7110 rect, str, strlen(str), NULL);
\r
7111 if(logoHeight > 0 && appData.clockMode) {
\r
7113 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7114 r.top = rect->top + logoHeight/2;
\r
7115 r.left = rect->left;
\r
7116 r.right = rect->right;
\r
7117 r.bottom = rect->bottom;
\r
7118 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7119 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7120 &r, str, strlen(str), NULL);
\r
7122 (void) SetTextColor(hdc, oldFg);
\r
7123 (void) SetBkColor(hdc, oldBg);
\r
7124 (void) SelectObject(hdc, oldFont);
\r
7129 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7135 if( count <= 0 ) {
\r
7136 if (appData.debugMode) {
\r
7137 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7140 return ERROR_INVALID_USER_BUFFER;
\r
7143 ResetEvent(ovl->hEvent);
\r
7144 ovl->Offset = ovl->OffsetHigh = 0;
\r
7145 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7149 err = GetLastError();
\r
7150 if (err == ERROR_IO_PENDING) {
\r
7151 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7155 err = GetLastError();
\r
7162 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7167 ResetEvent(ovl->hEvent);
\r
7168 ovl->Offset = ovl->OffsetHigh = 0;
\r
7169 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7173 err = GetLastError();
\r
7174 if (err == ERROR_IO_PENDING) {
\r
7175 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7179 err = GetLastError();
\r
7185 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7186 void CheckForInputBufferFull( InputSource * is )
\r
7188 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7189 /* Look for end of line */
\r
7190 char * p = is->buf;
\r
7192 while( p < is->next && *p != '\n' ) {
\r
7196 if( p >= is->next ) {
\r
7197 if (appData.debugMode) {
\r
7198 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7201 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7202 is->count = (DWORD) -1;
\r
7203 is->next = is->buf;
\r
7209 InputThread(LPVOID arg)
\r
7214 is = (InputSource *) arg;
\r
7215 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7216 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7217 while (is->hThread != NULL) {
\r
7218 is->error = DoReadFile(is->hFile, is->next,
\r
7219 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7220 &is->count, &ovl);
\r
7221 if (is->error == NO_ERROR) {
\r
7222 is->next += is->count;
\r
7224 if (is->error == ERROR_BROKEN_PIPE) {
\r
7225 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7228 is->count = (DWORD) -1;
\r
7229 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7234 CheckForInputBufferFull( is );
\r
7236 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7238 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7240 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7243 CloseHandle(ovl.hEvent);
\r
7244 CloseHandle(is->hFile);
\r
7246 if (appData.debugMode) {
\r
7247 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7254 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7256 NonOvlInputThread(LPVOID arg)
\r
7263 is = (InputSource *) arg;
\r
7264 while (is->hThread != NULL) {
\r
7265 is->error = ReadFile(is->hFile, is->next,
\r
7266 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7267 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7268 if (is->error == NO_ERROR) {
\r
7269 /* Change CRLF to LF */
\r
7270 if (is->next > is->buf) {
\r
7272 i = is->count + 1;
\r
7280 if (prev == '\r' && *p == '\n') {
\r
7292 if (is->error == ERROR_BROKEN_PIPE) {
\r
7293 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7296 is->count = (DWORD) -1;
\r
7300 CheckForInputBufferFull( is );
\r
7302 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7304 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7306 if (is->count < 0) break; /* Quit on error */
\r
7308 CloseHandle(is->hFile);
\r
7313 SocketInputThread(LPVOID arg)
\r
7317 is = (InputSource *) arg;
\r
7318 while (is->hThread != NULL) {
\r
7319 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7320 if ((int)is->count == SOCKET_ERROR) {
\r
7321 is->count = (DWORD) -1;
\r
7322 is->error = WSAGetLastError();
\r
7324 is->error = NO_ERROR;
\r
7325 is->next += is->count;
\r
7326 if (is->count == 0 && is->second == is) {
\r
7327 /* End of file on stderr; quit with no message */
\r
7331 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7333 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7335 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7341 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7345 is = (InputSource *) lParam;
\r
7346 if (is->lineByLine) {
\r
7347 /* Feed in lines one by one */
\r
7348 char *p = is->buf;
\r
7350 while (q < is->next) {
\r
7351 if (*q++ == '\n') {
\r
7352 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7357 /* Move any partial line to the start of the buffer */
\r
7359 while (p < is->next) {
\r
7364 if (is->error != NO_ERROR || is->count == 0) {
\r
7365 /* Notify backend of the error. Note: If there was a partial
\r
7366 line at the end, it is not flushed through. */
\r
7367 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7370 /* Feed in the whole chunk of input at once */
\r
7371 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7372 is->next = is->buf;
\r
7376 /*---------------------------------------------------------------------------*\
\r
7378 * Menu enables. Used when setting various modes.
\r
7380 \*---------------------------------------------------------------------------*/
\r
7388 GreyRevert(Boolean grey)
\r
7389 { // [HGM] vari: for retracting variations in local mode
\r
7390 HMENU hmenu = GetMenu(hwndMain);
\r
7391 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7392 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7396 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7398 while (enab->item > 0) {
\r
7399 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7404 Enables gnuEnables[] = {
\r
7405 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7406 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7407 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7408 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7409 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7410 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7411 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7412 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7413 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7415 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7416 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7417 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7421 Enables icsEnables[] = {
\r
7422 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7424 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7425 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7426 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7427 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7428 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7429 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7435 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7437 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7442 Enables zippyEnables[] = {
\r
7443 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7444 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7445 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7446 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7451 Enables ncpEnables[] = {
\r
7452 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7453 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7454 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7455 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7456 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7457 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7458 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7459 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7460 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7461 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7463 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7464 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7465 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7466 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7467 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7471 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7475 Enables trainingOnEnables[] = {
\r
7476 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7477 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7479 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7480 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7481 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7482 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7483 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7487 Enables trainingOffEnables[] = {
\r
7488 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7489 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7490 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7491 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7492 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7493 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7494 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7495 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7499 /* These modify either ncpEnables or gnuEnables */
\r
7500 Enables cmailEnables[] = {
\r
7501 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7502 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7503 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7504 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7505 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7506 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7507 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7511 Enables machineThinkingEnables[] = {
\r
7512 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7513 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7514 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7515 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7516 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7517 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7518 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7519 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7520 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7521 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7522 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7523 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7524 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7525 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7526 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7530 Enables userThinkingEnables[] = {
\r
7531 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7532 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7533 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7534 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7535 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7536 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7537 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7538 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7539 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7540 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7541 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7542 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7543 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7544 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7545 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7549 /*---------------------------------------------------------------------------*\
\r
7551 * Front-end interface functions exported by XBoard.
\r
7552 * Functions appear in same order as prototypes in frontend.h.
\r
7554 \*---------------------------------------------------------------------------*/
\r
7558 static UINT prevChecked = 0;
\r
7559 static int prevPausing = 0;
\r
7562 if (pausing != prevPausing) {
\r
7563 prevPausing = pausing;
\r
7564 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7565 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7566 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7569 switch (gameMode) {
\r
7570 case BeginningOfGame:
\r
7571 if (appData.icsActive)
\r
7572 nowChecked = IDM_IcsClient;
\r
7573 else if (appData.noChessProgram)
\r
7574 nowChecked = IDM_EditGame;
\r
7576 nowChecked = IDM_MachineBlack;
\r
7578 case MachinePlaysBlack:
\r
7579 nowChecked = IDM_MachineBlack;
\r
7581 case MachinePlaysWhite:
\r
7582 nowChecked = IDM_MachineWhite;
\r
7584 case TwoMachinesPlay:
\r
7585 nowChecked = IDM_TwoMachines;
\r
7588 nowChecked = IDM_AnalysisMode;
\r
7591 nowChecked = IDM_AnalyzeFile;
\r
7594 nowChecked = IDM_EditGame;
\r
7596 case PlayFromGameFile:
\r
7597 nowChecked = IDM_LoadGame;
\r
7599 case EditPosition:
\r
7600 nowChecked = IDM_EditPosition;
\r
7603 nowChecked = IDM_Training;
\r
7605 case IcsPlayingWhite:
\r
7606 case IcsPlayingBlack:
\r
7607 case IcsObserving:
\r
7609 nowChecked = IDM_IcsClient;
\r
7616 if (prevChecked != 0)
\r
7617 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7618 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7619 if (nowChecked != 0)
\r
7620 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7621 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7623 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7624 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7625 MF_BYCOMMAND|MF_ENABLED);
\r
7627 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7628 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7631 prevChecked = nowChecked;
\r
7633 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7634 if (appData.icsActive) {
\r
7635 if (appData.icsEngineAnalyze) {
\r
7636 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7637 MF_BYCOMMAND|MF_CHECKED);
\r
7639 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7640 MF_BYCOMMAND|MF_UNCHECKED);
\r
7648 HMENU hmenu = GetMenu(hwndMain);
\r
7649 SetMenuEnables(hmenu, icsEnables);
\r
7650 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7651 MF_BYPOSITION|MF_ENABLED);
\r
7653 if (appData.zippyPlay) {
\r
7654 SetMenuEnables(hmenu, zippyEnables);
\r
7655 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7656 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7657 MF_BYCOMMAND|MF_ENABLED);
\r
7665 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7671 HMENU hmenu = GetMenu(hwndMain);
\r
7672 SetMenuEnables(hmenu, ncpEnables);
\r
7673 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7674 MF_BYPOSITION|MF_GRAYED);
\r
7675 DrawMenuBar(hwndMain);
\r
7681 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7685 SetTrainingModeOn()
\r
7688 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7689 for (i = 0; i < N_BUTTONS; i++) {
\r
7690 if (buttonDesc[i].hwnd != NULL)
\r
7691 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7696 VOID SetTrainingModeOff()
\r
7699 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7700 for (i = 0; i < N_BUTTONS; i++) {
\r
7701 if (buttonDesc[i].hwnd != NULL)
\r
7702 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7708 SetUserThinkingEnables()
\r
7710 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7714 SetMachineThinkingEnables()
\r
7716 HMENU hMenu = GetMenu(hwndMain);
\r
7717 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7719 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7721 if (gameMode == MachinePlaysBlack) {
\r
7722 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7723 } else if (gameMode == MachinePlaysWhite) {
\r
7724 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7725 } else if (gameMode == TwoMachinesPlay) {
\r
7726 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7732 DisplayTitle(char *str)
\r
7734 char title[MSG_SIZ], *host;
\r
7735 if (str[0] != NULLCHAR) {
\r
7736 strcpy(title, str);
\r
7737 } else if (appData.icsActive) {
\r
7738 if (appData.icsCommPort[0] != NULLCHAR)
\r
7741 host = appData.icsHost;
\r
7742 sprintf(title, "%s: %s", szTitle, host);
\r
7743 } else if (appData.noChessProgram) {
\r
7744 strcpy(title, szTitle);
\r
7746 strcpy(title, szTitle);
\r
7747 strcat(title, ": ");
\r
7748 strcat(title, first.tidy);
\r
7750 SetWindowText(hwndMain, title);
\r
7755 DisplayMessage(char *str1, char *str2)
\r
7759 int remain = MESSAGE_TEXT_MAX - 1;
\r
7762 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7763 messageText[0] = NULLCHAR;
\r
7765 len = strlen(str1);
\r
7766 if (len > remain) len = remain;
\r
7767 strncpy(messageText, str1, len);
\r
7768 messageText[len] = NULLCHAR;
\r
7771 if (*str2 && remain >= 2) {
\r
7773 strcat(messageText, " ");
\r
7776 len = strlen(str2);
\r
7777 if (len > remain) len = remain;
\r
7778 strncat(messageText, str2, len);
\r
7780 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7782 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7786 hdc = GetDC(hwndMain);
\r
7787 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7788 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7789 &messageRect, messageText, strlen(messageText), NULL);
\r
7790 (void) SelectObject(hdc, oldFont);
\r
7791 (void) ReleaseDC(hwndMain, hdc);
\r
7795 DisplayError(char *str, int error)
\r
7797 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7803 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7804 NULL, error, LANG_NEUTRAL,
\r
7805 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7807 sprintf(buf, "%s:\n%s", str, buf2);
\r
7809 ErrorMap *em = errmap;
\r
7810 while (em->err != 0 && em->err != error) em++;
\r
7811 if (em->err != 0) {
\r
7812 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7814 sprintf(buf, "%s:\nError code %d", str, error);
\r
7819 ErrorPopUp("Error", buf);
\r
7824 DisplayMoveError(char *str)
\r
7826 fromX = fromY = -1;
\r
7827 ClearHighlights();
\r
7828 DrawPosition(FALSE, NULL);
\r
7829 if (appData.popupMoveErrors) {
\r
7830 ErrorPopUp("Error", str);
\r
7832 DisplayMessage(str, "");
\r
7833 moveErrorMessageUp = TRUE;
\r
7838 DisplayFatalError(char *str, int error, int exitStatus)
\r
7840 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7842 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7845 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7846 NULL, error, LANG_NEUTRAL,
\r
7847 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7849 sprintf(buf, "%s:\n%s", str, buf2);
\r
7851 ErrorMap *em = errmap;
\r
7852 while (em->err != 0 && em->err != error) em++;
\r
7853 if (em->err != 0) {
\r
7854 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7856 sprintf(buf, "%s:\nError code %d", str, error);
\r
7861 if (appData.debugMode) {
\r
7862 fprintf(debugFP, "%s: %s\n", label, str);
\r
7864 if (appData.popupExitMessage) {
\r
7865 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7866 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7868 ExitEvent(exitStatus);
\r
7873 DisplayInformation(char *str)
\r
7875 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7880 DisplayNote(char *str)
\r
7882 ErrorPopUp("Note", str);
\r
7887 char *title, *question, *replyPrefix;
\r
7892 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7894 static QuestionParams *qp;
\r
7895 char reply[MSG_SIZ];
\r
7898 switch (message) {
\r
7899 case WM_INITDIALOG:
\r
7900 qp = (QuestionParams *) lParam;
\r
7901 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7902 SetWindowText(hDlg, qp->title);
\r
7903 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7904 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7908 switch (LOWORD(wParam)) {
\r
7910 strcpy(reply, qp->replyPrefix);
\r
7911 if (*reply) strcat(reply, " ");
\r
7912 len = strlen(reply);
\r
7913 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7914 strcat(reply, "\n");
\r
7915 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7916 EndDialog(hDlg, TRUE);
\r
7917 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7920 EndDialog(hDlg, FALSE);
\r
7931 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7933 QuestionParams qp;
\r
7937 qp.question = question;
\r
7938 qp.replyPrefix = replyPrefix;
\r
7940 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7941 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7942 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7943 FreeProcInstance(lpProc);
\r
7946 /* [AS] Pick FRC position */
\r
7947 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7949 static int * lpIndexFRC;
\r
7955 case WM_INITDIALOG:
\r
7956 lpIndexFRC = (int *) lParam;
\r
7958 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7960 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7961 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7962 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7963 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7968 switch( LOWORD(wParam) ) {
\r
7970 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7971 EndDialog( hDlg, 0 );
\r
7972 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7975 EndDialog( hDlg, 1 );
\r
7977 case IDC_NFG_Edit:
\r
7978 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7979 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7981 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7984 case IDC_NFG_Random:
\r
7985 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7986 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7999 int index = appData.defaultFrcPosition;
\r
8000 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8002 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8004 if( result == 0 ) {
\r
8005 appData.defaultFrcPosition = index;
\r
8011 /* [AS] Game list options. Refactored by HGM */
\r
8013 HWND gameListOptionsDialog;
\r
8015 // low-level front-end: clear text edit / list widget
\r
8019 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8022 // low-level front-end: clear text edit / list widget
\r
8024 GLT_DeSelectList()
\r
8026 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8029 // low-level front-end: append line to text edit / list widget
\r
8031 GLT_AddToList( char *name )
\r
8034 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8038 // low-level front-end: get line from text edit / list widget
\r
8040 GLT_GetFromList( int index, char *name )
\r
8043 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8049 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8051 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8052 int idx2 = idx1 + delta;
\r
8053 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8055 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8058 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8059 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8060 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8061 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8065 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8069 case WM_INITDIALOG:
\r
8070 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8072 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8074 /* Initialize list */
\r
8075 GLT_TagsToList( lpUserGLT );
\r
8077 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8082 switch( LOWORD(wParam) ) {
\r
8085 EndDialog( hDlg, 0 );
\r
8088 EndDialog( hDlg, 1 );
\r
8091 case IDC_GLT_Default:
\r
8092 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8095 case IDC_GLT_Restore:
\r
8096 GLT_TagsToList( appData.gameListTags );
\r
8100 GLT_MoveSelection( hDlg, -1 );
\r
8103 case IDC_GLT_Down:
\r
8104 GLT_MoveSelection( hDlg, +1 );
\r
8114 int GameListOptions()
\r
8117 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8119 strcpy( lpUserGLT, appData.gameListTags );
\r
8121 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8123 if( result == 0 ) {
\r
8124 /* [AS] Memory leak here! */
\r
8125 appData.gameListTags = strdup( lpUserGLT );
\r
8132 DisplayIcsInteractionTitle(char *str)
\r
8134 char consoleTitle[MSG_SIZ];
\r
8136 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8137 SetWindowText(hwndConsole, consoleTitle);
\r
8141 DrawPosition(int fullRedraw, Board board)
\r
8143 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8146 void NotifyFrontendLogin()
\r
8149 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8155 fromX = fromY = -1;
\r
8156 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8157 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8158 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8159 dragInfo.lastpos = dragInfo.pos;
\r
8160 dragInfo.start.x = dragInfo.start.y = -1;
\r
8161 dragInfo.from = dragInfo.start;
\r
8163 DrawPosition(TRUE, NULL);
\r
8169 CommentPopUp(char *title, char *str)
\r
8171 HWND hwnd = GetActiveWindow();
\r
8172 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8174 SetActiveWindow(hwnd);
\r
8178 CommentPopDown(void)
\r
8180 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8181 if (commentDialog) {
\r
8182 ShowWindow(commentDialog, SW_HIDE);
\r
8184 commentUp = FALSE;
\r
8188 EditCommentPopUp(int index, char *title, char *str)
\r
8190 EitherCommentPopUp(index, title, str, TRUE);
\r
8197 MyPlaySound(&sounds[(int)SoundMove]);
\r
8200 VOID PlayIcsWinSound()
\r
8202 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8205 VOID PlayIcsLossSound()
\r
8207 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8210 VOID PlayIcsDrawSound()
\r
8212 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8215 VOID PlayIcsUnfinishedSound()
\r
8217 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8223 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8231 consoleEcho = TRUE;
\r
8232 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8233 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8234 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8243 consoleEcho = FALSE;
\r
8244 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8245 /* This works OK: set text and background both to the same color */
\r
8247 cf.crTextColor = COLOR_ECHOOFF;
\r
8248 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8249 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8252 /* No Raw()...? */
\r
8254 void Colorize(ColorClass cc, int continuation)
\r
8256 currentColorClass = cc;
\r
8257 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8258 consoleCF.crTextColor = textAttribs[cc].color;
\r
8259 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8260 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8266 static char buf[MSG_SIZ];
\r
8267 DWORD bufsiz = MSG_SIZ;
\r
8269 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8270 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8272 if (!GetUserName(buf, &bufsiz)) {
\r
8273 /*DisplayError("Error getting user name", GetLastError());*/
\r
8274 strcpy(buf, "User");
\r
8282 static char buf[MSG_SIZ];
\r
8283 DWORD bufsiz = MSG_SIZ;
\r
8285 if (!GetComputerName(buf, &bufsiz)) {
\r
8286 /*DisplayError("Error getting host name", GetLastError());*/
\r
8287 strcpy(buf, "Unknown");
\r
8294 ClockTimerRunning()
\r
8296 return clockTimerEvent != 0;
\r
8302 if (clockTimerEvent == 0) return FALSE;
\r
8303 KillTimer(hwndMain, clockTimerEvent);
\r
8304 clockTimerEvent = 0;
\r
8309 StartClockTimer(long millisec)
\r
8311 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8312 (UINT) millisec, NULL);
\r
8316 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8319 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8321 if(appData.noGUI) return;
\r
8322 hdc = GetDC(hwndMain);
\r
8323 if (!IsIconic(hwndMain)) {
\r
8324 DisplayAClock(hdc, timeRemaining, highlight,
\r
8325 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8327 if (highlight && iconCurrent == iconBlack) {
\r
8328 iconCurrent = iconWhite;
\r
8329 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8330 if (IsIconic(hwndMain)) {
\r
8331 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8334 (void) ReleaseDC(hwndMain, hdc);
\r
8336 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8340 DisplayBlackClock(long timeRemaining, int highlight)
\r
8343 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8345 if(appData.noGUI) return;
\r
8346 hdc = GetDC(hwndMain);
\r
8347 if (!IsIconic(hwndMain)) {
\r
8348 DisplayAClock(hdc, timeRemaining, highlight,
\r
8349 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8351 if (highlight && iconCurrent == iconWhite) {
\r
8352 iconCurrent = iconBlack;
\r
8353 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8354 if (IsIconic(hwndMain)) {
\r
8355 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8358 (void) ReleaseDC(hwndMain, hdc);
\r
8360 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8365 LoadGameTimerRunning()
\r
8367 return loadGameTimerEvent != 0;
\r
8371 StopLoadGameTimer()
\r
8373 if (loadGameTimerEvent == 0) return FALSE;
\r
8374 KillTimer(hwndMain, loadGameTimerEvent);
\r
8375 loadGameTimerEvent = 0;
\r
8380 StartLoadGameTimer(long millisec)
\r
8382 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8383 (UINT) millisec, NULL);
\r
8391 char fileTitle[MSG_SIZ];
\r
8393 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8394 f = OpenFileDialog(hwndMain, "a", defName,
\r
8395 appData.oldSaveStyle ? "gam" : "pgn",
\r
8397 "Save Game to File", NULL, fileTitle, NULL);
\r
8399 SaveGame(f, 0, "");
\r
8406 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8408 if (delayedTimerEvent != 0) {
\r
8409 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8410 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8412 KillTimer(hwndMain, delayedTimerEvent);
\r
8413 delayedTimerEvent = 0;
\r
8414 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8415 delayedTimerCallback();
\r
8417 delayedTimerCallback = cb;
\r
8418 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8419 (UINT) millisec, NULL);
\r
8422 DelayedEventCallback
\r
8425 if (delayedTimerEvent) {
\r
8426 return delayedTimerCallback;
\r
8433 CancelDelayedEvent()
\r
8435 if (delayedTimerEvent) {
\r
8436 KillTimer(hwndMain, delayedTimerEvent);
\r
8437 delayedTimerEvent = 0;
\r
8441 DWORD GetWin32Priority(int nice)
\r
8442 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8444 REALTIME_PRIORITY_CLASS 0x00000100
\r
8445 HIGH_PRIORITY_CLASS 0x00000080
\r
8446 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8447 NORMAL_PRIORITY_CLASS 0x00000020
\r
8448 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8449 IDLE_PRIORITY_CLASS 0x00000040
\r
8451 if (nice < -15) return 0x00000080;
\r
8452 if (nice < 0) return 0x00008000;
\r
8453 if (nice == 0) return 0x00000020;
\r
8454 if (nice < 15) return 0x00004000;
\r
8455 return 0x00000040;
\r
8458 /* Start a child process running the given program.
\r
8459 The process's standard output can be read from "from", and its
\r
8460 standard input can be written to "to".
\r
8461 Exit with fatal error if anything goes wrong.
\r
8462 Returns an opaque pointer that can be used to destroy the process
\r
8466 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8468 #define BUFSIZE 4096
\r
8470 HANDLE hChildStdinRd, hChildStdinWr,
\r
8471 hChildStdoutRd, hChildStdoutWr;
\r
8472 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8473 SECURITY_ATTRIBUTES saAttr;
\r
8475 PROCESS_INFORMATION piProcInfo;
\r
8476 STARTUPINFO siStartInfo;
\r
8478 char buf[MSG_SIZ];
\r
8481 if (appData.debugMode) {
\r
8482 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8487 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8488 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8489 saAttr.bInheritHandle = TRUE;
\r
8490 saAttr.lpSecurityDescriptor = NULL;
\r
8493 * The steps for redirecting child's STDOUT:
\r
8494 * 1. Create anonymous pipe to be STDOUT for child.
\r
8495 * 2. Create a noninheritable duplicate of read handle,
\r
8496 * and close the inheritable read handle.
\r
8499 /* Create a pipe for the child's STDOUT. */
\r
8500 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8501 return GetLastError();
\r
8504 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8505 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8506 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8507 FALSE, /* not inherited */
\r
8508 DUPLICATE_SAME_ACCESS);
\r
8510 return GetLastError();
\r
8512 CloseHandle(hChildStdoutRd);
\r
8515 * The steps for redirecting child's STDIN:
\r
8516 * 1. Create anonymous pipe to be STDIN for child.
\r
8517 * 2. Create a noninheritable duplicate of write handle,
\r
8518 * and close the inheritable write handle.
\r
8521 /* Create a pipe for the child's STDIN. */
\r
8522 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8523 return GetLastError();
\r
8526 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8527 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8528 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8529 FALSE, /* not inherited */
\r
8530 DUPLICATE_SAME_ACCESS);
\r
8532 return GetLastError();
\r
8534 CloseHandle(hChildStdinWr);
\r
8536 /* Arrange to (1) look in dir for the child .exe file, and
\r
8537 * (2) have dir be the child's working directory. Interpret
\r
8538 * dir relative to the directory WinBoard loaded from. */
\r
8539 GetCurrentDirectory(MSG_SIZ, buf);
\r
8540 SetCurrentDirectory(installDir);
\r
8541 SetCurrentDirectory(dir);
\r
8543 /* Now create the child process. */
\r
8545 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8546 siStartInfo.lpReserved = NULL;
\r
8547 siStartInfo.lpDesktop = NULL;
\r
8548 siStartInfo.lpTitle = NULL;
\r
8549 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8550 siStartInfo.cbReserved2 = 0;
\r
8551 siStartInfo.lpReserved2 = NULL;
\r
8552 siStartInfo.hStdInput = hChildStdinRd;
\r
8553 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8554 siStartInfo.hStdError = hChildStdoutWr;
\r
8556 fSuccess = CreateProcess(NULL,
\r
8557 cmdLine, /* command line */
\r
8558 NULL, /* process security attributes */
\r
8559 NULL, /* primary thread security attrs */
\r
8560 TRUE, /* handles are inherited */
\r
8561 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8562 NULL, /* use parent's environment */
\r
8564 &siStartInfo, /* STARTUPINFO pointer */
\r
8565 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8567 err = GetLastError();
\r
8568 SetCurrentDirectory(buf); /* return to prev directory */
\r
8573 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8574 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8575 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8578 /* Close the handles we don't need in the parent */
\r
8579 CloseHandle(piProcInfo.hThread);
\r
8580 CloseHandle(hChildStdinRd);
\r
8581 CloseHandle(hChildStdoutWr);
\r
8583 /* Prepare return value */
\r
8584 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8585 cp->kind = CPReal;
\r
8586 cp->hProcess = piProcInfo.hProcess;
\r
8587 cp->pid = piProcInfo.dwProcessId;
\r
8588 cp->hFrom = hChildStdoutRdDup;
\r
8589 cp->hTo = hChildStdinWrDup;
\r
8591 *pr = (void *) cp;
\r
8593 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8594 2000 where engines sometimes don't see the initial command(s)
\r
8595 from WinBoard and hang. I don't understand how that can happen,
\r
8596 but the Sleep is harmless, so I've put it in. Others have also
\r
8597 reported what may be the same problem, so hopefully this will fix
\r
8598 it for them too. */
\r
8606 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8608 ChildProc *cp; int result;
\r
8610 cp = (ChildProc *) pr;
\r
8611 if (cp == NULL) return;
\r
8613 switch (cp->kind) {
\r
8615 /* TerminateProcess is considered harmful, so... */
\r
8616 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8617 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8618 /* The following doesn't work because the chess program
\r
8619 doesn't "have the same console" as WinBoard. Maybe
\r
8620 we could arrange for this even though neither WinBoard
\r
8621 nor the chess program uses a console for stdio? */
\r
8622 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8624 /* [AS] Special termination modes for misbehaving programs... */
\r
8625 if( signal == 9 ) {
\r
8626 result = TerminateProcess( cp->hProcess, 0 );
\r
8628 if ( appData.debugMode) {
\r
8629 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8632 else if( signal == 10 ) {
\r
8633 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8635 if( dw != WAIT_OBJECT_0 ) {
\r
8636 result = TerminateProcess( cp->hProcess, 0 );
\r
8638 if ( appData.debugMode) {
\r
8639 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8645 CloseHandle(cp->hProcess);
\r
8649 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8653 closesocket(cp->sock);
\r
8658 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8659 closesocket(cp->sock);
\r
8660 closesocket(cp->sock2);
\r
8668 InterruptChildProcess(ProcRef pr)
\r
8672 cp = (ChildProc *) pr;
\r
8673 if (cp == NULL) return;
\r
8674 switch (cp->kind) {
\r
8676 /* The following doesn't work because the chess program
\r
8677 doesn't "have the same console" as WinBoard. Maybe
\r
8678 we could arrange for this even though neither WinBoard
\r
8679 nor the chess program uses a console for stdio */
\r
8680 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8685 /* Can't interrupt */
\r
8689 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8696 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8698 char cmdLine[MSG_SIZ];
\r
8700 if (port[0] == NULLCHAR) {
\r
8701 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8703 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8705 return StartChildProcess(cmdLine, "", pr);
\r
8709 /* Code to open TCP sockets */
\r
8712 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8717 struct sockaddr_in sa, mysa;
\r
8718 struct hostent FAR *hp;
\r
8719 unsigned short uport;
\r
8720 WORD wVersionRequested;
\r
8723 /* Initialize socket DLL */
\r
8724 wVersionRequested = MAKEWORD(1, 1);
\r
8725 err = WSAStartup(wVersionRequested, &wsaData);
\r
8726 if (err != 0) return err;
\r
8729 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8730 err = WSAGetLastError();
\r
8735 /* Bind local address using (mostly) don't-care values.
\r
8737 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8738 mysa.sin_family = AF_INET;
\r
8739 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8740 uport = (unsigned short) 0;
\r
8741 mysa.sin_port = htons(uport);
\r
8742 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8743 == SOCKET_ERROR) {
\r
8744 err = WSAGetLastError();
\r
8749 /* Resolve remote host name */
\r
8750 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8751 if (!(hp = gethostbyname(host))) {
\r
8752 unsigned int b0, b1, b2, b3;
\r
8754 err = WSAGetLastError();
\r
8756 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8757 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8758 hp->h_addrtype = AF_INET;
\r
8760 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8761 hp->h_addr_list[0] = (char *) malloc(4);
\r
8762 hp->h_addr_list[0][0] = (char) b0;
\r
8763 hp->h_addr_list[0][1] = (char) b1;
\r
8764 hp->h_addr_list[0][2] = (char) b2;
\r
8765 hp->h_addr_list[0][3] = (char) b3;
\r
8771 sa.sin_family = hp->h_addrtype;
\r
8772 uport = (unsigned short) atoi(port);
\r
8773 sa.sin_port = htons(uport);
\r
8774 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8776 /* Make connection */
\r
8777 if (connect(s, (struct sockaddr *) &sa,
\r
8778 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8779 err = WSAGetLastError();
\r
8784 /* Prepare return value */
\r
8785 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8786 cp->kind = CPSock;
\r
8788 *pr = (ProcRef *) cp;
\r
8794 OpenCommPort(char *name, ProcRef *pr)
\r
8799 char fullname[MSG_SIZ];
\r
8801 if (*name != '\\')
\r
8802 sprintf(fullname, "\\\\.\\%s", name);
\r
8804 strcpy(fullname, name);
\r
8806 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8807 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8808 if (h == (HANDLE) -1) {
\r
8809 return GetLastError();
\r
8813 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8815 /* Accumulate characters until a 100ms pause, then parse */
\r
8816 ct.ReadIntervalTimeout = 100;
\r
8817 ct.ReadTotalTimeoutMultiplier = 0;
\r
8818 ct.ReadTotalTimeoutConstant = 0;
\r
8819 ct.WriteTotalTimeoutMultiplier = 0;
\r
8820 ct.WriteTotalTimeoutConstant = 0;
\r
8821 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8823 /* Prepare return value */
\r
8824 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8825 cp->kind = CPComm;
\r
8828 *pr = (ProcRef *) cp;
\r
8834 OpenLoopback(ProcRef *pr)
\r
8836 DisplayFatalError("Not implemented", 0, 1);
\r
8842 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8847 struct sockaddr_in sa, mysa;
\r
8848 struct hostent FAR *hp;
\r
8849 unsigned short uport;
\r
8850 WORD wVersionRequested;
\r
8853 char stderrPortStr[MSG_SIZ];
\r
8855 /* Initialize socket DLL */
\r
8856 wVersionRequested = MAKEWORD(1, 1);
\r
8857 err = WSAStartup(wVersionRequested, &wsaData);
\r
8858 if (err != 0) return err;
\r
8860 /* Resolve remote host name */
\r
8861 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8862 if (!(hp = gethostbyname(host))) {
\r
8863 unsigned int b0, b1, b2, b3;
\r
8865 err = WSAGetLastError();
\r
8867 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8868 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8869 hp->h_addrtype = AF_INET;
\r
8871 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8872 hp->h_addr_list[0] = (char *) malloc(4);
\r
8873 hp->h_addr_list[0][0] = (char) b0;
\r
8874 hp->h_addr_list[0][1] = (char) b1;
\r
8875 hp->h_addr_list[0][2] = (char) b2;
\r
8876 hp->h_addr_list[0][3] = (char) b3;
\r
8882 sa.sin_family = hp->h_addrtype;
\r
8883 uport = (unsigned short) 514;
\r
8884 sa.sin_port = htons(uport);
\r
8885 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8887 /* Bind local socket to unused "privileged" port address
\r
8889 s = INVALID_SOCKET;
\r
8890 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8891 mysa.sin_family = AF_INET;
\r
8892 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8893 for (fromPort = 1023;; fromPort--) {
\r
8894 if (fromPort < 0) {
\r
8896 return WSAEADDRINUSE;
\r
8898 if (s == INVALID_SOCKET) {
\r
8899 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8900 err = WSAGetLastError();
\r
8905 uport = (unsigned short) fromPort;
\r
8906 mysa.sin_port = htons(uport);
\r
8907 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8908 == SOCKET_ERROR) {
\r
8909 err = WSAGetLastError();
\r
8910 if (err == WSAEADDRINUSE) continue;
\r
8914 if (connect(s, (struct sockaddr *) &sa,
\r
8915 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8916 err = WSAGetLastError();
\r
8917 if (err == WSAEADDRINUSE) {
\r
8928 /* Bind stderr local socket to unused "privileged" port address
\r
8930 s2 = INVALID_SOCKET;
\r
8931 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8932 mysa.sin_family = AF_INET;
\r
8933 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8934 for (fromPort = 1023;; fromPort--) {
\r
8935 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8936 if (fromPort < 0) {
\r
8937 (void) closesocket(s);
\r
8939 return WSAEADDRINUSE;
\r
8941 if (s2 == INVALID_SOCKET) {
\r
8942 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8943 err = WSAGetLastError();
\r
8949 uport = (unsigned short) fromPort;
\r
8950 mysa.sin_port = htons(uport);
\r
8951 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8952 == SOCKET_ERROR) {
\r
8953 err = WSAGetLastError();
\r
8954 if (err == WSAEADDRINUSE) continue;
\r
8955 (void) closesocket(s);
\r
8959 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8960 err = WSAGetLastError();
\r
8961 if (err == WSAEADDRINUSE) {
\r
8963 s2 = INVALID_SOCKET;
\r
8966 (void) closesocket(s);
\r
8967 (void) closesocket(s2);
\r
8973 prevStderrPort = fromPort; // remember port used
\r
8974 sprintf(stderrPortStr, "%d", fromPort);
\r
8976 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8977 err = WSAGetLastError();
\r
8978 (void) closesocket(s);
\r
8979 (void) closesocket(s2);
\r
8984 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8985 err = WSAGetLastError();
\r
8986 (void) closesocket(s);
\r
8987 (void) closesocket(s2);
\r
8991 if (*user == NULLCHAR) user = UserName();
\r
8992 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8993 err = WSAGetLastError();
\r
8994 (void) closesocket(s);
\r
8995 (void) closesocket(s2);
\r
8999 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9000 err = WSAGetLastError();
\r
9001 (void) closesocket(s);
\r
9002 (void) closesocket(s2);
\r
9007 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9008 err = WSAGetLastError();
\r
9009 (void) closesocket(s);
\r
9010 (void) closesocket(s2);
\r
9014 (void) closesocket(s2); /* Stop listening */
\r
9016 /* Prepare return value */
\r
9017 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9018 cp->kind = CPRcmd;
\r
9021 *pr = (ProcRef *) cp;
\r
9028 AddInputSource(ProcRef pr, int lineByLine,
\r
9029 InputCallback func, VOIDSTAR closure)
\r
9031 InputSource *is, *is2 = NULL;
\r
9032 ChildProc *cp = (ChildProc *) pr;
\r
9034 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9035 is->lineByLine = lineByLine;
\r
9037 is->closure = closure;
\r
9038 is->second = NULL;
\r
9039 is->next = is->buf;
\r
9040 if (pr == NoProc) {
\r
9041 is->kind = CPReal;
\r
9042 consoleInputSource = is;
\r
9044 is->kind = cp->kind;
\r
9046 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9047 we create all threads suspended so that the is->hThread variable can be
\r
9048 safely assigned, then let the threads start with ResumeThread.
\r
9050 switch (cp->kind) {
\r
9052 is->hFile = cp->hFrom;
\r
9053 cp->hFrom = NULL; /* now owned by InputThread */
\r
9055 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9056 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9060 is->hFile = cp->hFrom;
\r
9061 cp->hFrom = NULL; /* now owned by InputThread */
\r
9063 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9064 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9068 is->sock = cp->sock;
\r
9070 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9071 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9075 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9077 is->sock = cp->sock;
\r
9079 is2->sock = cp->sock2;
\r
9080 is2->second = is2;
\r
9082 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9083 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9085 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9086 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9090 if( is->hThread != NULL ) {
\r
9091 ResumeThread( is->hThread );
\r
9094 if( is2 != NULL && is2->hThread != NULL ) {
\r
9095 ResumeThread( is2->hThread );
\r
9099 return (InputSourceRef) is;
\r
9103 RemoveInputSource(InputSourceRef isr)
\r
9107 is = (InputSource *) isr;
\r
9108 is->hThread = NULL; /* tell thread to stop */
\r
9109 CloseHandle(is->hThread);
\r
9110 if (is->second != NULL) {
\r
9111 is->second->hThread = NULL;
\r
9112 CloseHandle(is->second->hThread);
\r
9116 int no_wrap(char *message, int count)
\r
9118 ConsoleOutput(message, count, FALSE);
\r
9123 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9126 int outCount = SOCKET_ERROR;
\r
9127 ChildProc *cp = (ChildProc *) pr;
\r
9128 static OVERLAPPED ovl;
\r
9129 static int line = 0;
\r
9133 if (appData.noJoin || !appData.useInternalWrap)
\r
9134 return no_wrap(message, count);
\r
9137 int width = get_term_width();
\r
9138 int len = wrap(NULL, message, count, width, &line);
\r
9139 char *msg = malloc(len);
\r
9143 return no_wrap(message, count);
\r
9146 dbgchk = wrap(msg, message, count, width, &line);
\r
9147 if (dbgchk != len && appData.debugMode)
\r
9148 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9149 ConsoleOutput(msg, len, FALSE);
\r
9156 if (ovl.hEvent == NULL) {
\r
9157 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9159 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9161 switch (cp->kind) {
\r
9164 outCount = send(cp->sock, message, count, 0);
\r
9165 if (outCount == SOCKET_ERROR) {
\r
9166 *outError = WSAGetLastError();
\r
9168 *outError = NO_ERROR;
\r
9173 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9174 &dOutCount, NULL)) {
\r
9175 *outError = NO_ERROR;
\r
9176 outCount = (int) dOutCount;
\r
9178 *outError = GetLastError();
\r
9183 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9184 &dOutCount, &ovl);
\r
9185 if (*outError == NO_ERROR) {
\r
9186 outCount = (int) dOutCount;
\r
9194 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9197 /* Ignore delay, not implemented for WinBoard */
\r
9198 return OutputToProcess(pr, message, count, outError);
\r
9203 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9204 char *buf, int count, int error)
\r
9206 DisplayFatalError("Not implemented", 0, 1);
\r
9209 /* see wgamelist.c for Game List functions */
\r
9210 /* see wedittags.c for Edit Tags functions */
\r
9217 char buf[MSG_SIZ];
\r
9220 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9221 f = fopen(buf, "r");
\r
9223 ProcessICSInitScript(f);
\r
9231 StartAnalysisClock()
\r
9233 if (analysisTimerEvent) return;
\r
9234 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9235 (UINT) 2000, NULL);
\r
9239 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9241 highlightInfo.sq[0].x = fromX;
\r
9242 highlightInfo.sq[0].y = fromY;
\r
9243 highlightInfo.sq[1].x = toX;
\r
9244 highlightInfo.sq[1].y = toY;
\r
9250 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9251 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9255 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9257 premoveHighlightInfo.sq[0].x = fromX;
\r
9258 premoveHighlightInfo.sq[0].y = fromY;
\r
9259 premoveHighlightInfo.sq[1].x = toX;
\r
9260 premoveHighlightInfo.sq[1].y = toY;
\r
9264 ClearPremoveHighlights()
\r
9266 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9267 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9271 ShutDownFrontEnd()
\r
9273 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9274 DeleteClipboardTempFiles();
\r
9280 if (IsIconic(hwndMain))
\r
9281 ShowWindow(hwndMain, SW_RESTORE);
\r
9283 SetActiveWindow(hwndMain);
\r
9287 * Prototypes for animation support routines
\r
9289 static void ScreenSquare(int column, int row, POINT * pt);
\r
9290 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9291 POINT frames[], int * nFrames);
\r
9295 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9296 { // [HGM] atomic: animate blast wave
\r
9298 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9299 explodeInfo.fromX = fromX;
\r
9300 explodeInfo.fromY = fromY;
\r
9301 explodeInfo.toX = toX;
\r
9302 explodeInfo.toY = toY;
\r
9303 for(i=1; i<nFrames; i++) {
\r
9304 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9305 DrawPosition(FALSE, NULL);
\r
9306 Sleep(appData.animSpeed);
\r
9308 explodeInfo.radius = 0;
\r
9309 DrawPosition(TRUE, NULL);
\r
9315 AnimateMove(board, fromX, fromY, toX, toY)
\r
9322 ChessSquare piece;
\r
9323 POINT start, finish, mid;
\r
9324 POINT frames[kFactor * 2 + 1];
\r
9327 if (!appData.animate) return;
\r
9328 if (doingSizing) return;
\r
9329 if (fromY < 0 || fromX < 0) return;
\r
9330 piece = board[fromY][fromX];
\r
9331 if (piece >= EmptySquare) return;
\r
9333 ScreenSquare(fromX, fromY, &start);
\r
9334 ScreenSquare(toX, toY, &finish);
\r
9336 /* All pieces except knights move in straight line */
\r
9337 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9338 mid.x = start.x + (finish.x - start.x) / 2;
\r
9339 mid.y = start.y + (finish.y - start.y) / 2;
\r
9341 /* Knight: make diagonal movement then straight */
\r
9342 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9343 mid.x = start.x + (finish.x - start.x) / 2;
\r
9347 mid.y = start.y + (finish.y - start.y) / 2;
\r
9351 /* Don't use as many frames for very short moves */
\r
9352 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9353 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9355 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9357 animInfo.from.x = fromX;
\r
9358 animInfo.from.y = fromY;
\r
9359 animInfo.to.x = toX;
\r
9360 animInfo.to.y = toY;
\r
9361 animInfo.lastpos = start;
\r
9362 animInfo.piece = piece;
\r
9363 for (n = 0; n < nFrames; n++) {
\r
9364 animInfo.pos = frames[n];
\r
9365 DrawPosition(FALSE, NULL);
\r
9366 animInfo.lastpos = animInfo.pos;
\r
9367 Sleep(appData.animSpeed);
\r
9369 animInfo.pos = finish;
\r
9370 DrawPosition(FALSE, NULL);
\r
9371 animInfo.piece = EmptySquare;
\r
9372 if(gameInfo.variant == VariantAtomic &&
\r
9373 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9374 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9377 /* Convert board position to corner of screen rect and color */
\r
9380 ScreenSquare(column, row, pt)
\r
9381 int column; int row; POINT * pt;
\r
9384 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9385 pt->y = lineGap + row * (squareSize + lineGap);
\r
9387 pt->x = lineGap + column * (squareSize + lineGap);
\r
9388 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9392 /* Generate a series of frame coords from start->mid->finish.
\r
9393 The movement rate doubles until the half way point is
\r
9394 reached, then halves back down to the final destination,
\r
9395 which gives a nice slow in/out effect. The algorithmn
\r
9396 may seem to generate too many intermediates for short
\r
9397 moves, but remember that the purpose is to attract the
\r
9398 viewers attention to the piece about to be moved and
\r
9399 then to where it ends up. Too few frames would be less
\r
9403 Tween(start, mid, finish, factor, frames, nFrames)
\r
9404 POINT * start; POINT * mid;
\r
9405 POINT * finish; int factor;
\r
9406 POINT frames[]; int * nFrames;
\r
9408 int n, fraction = 1, count = 0;
\r
9410 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9411 for (n = 0; n < factor; n++)
\r
9413 for (n = 0; n < factor; n++) {
\r
9414 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9415 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9417 fraction = fraction / 2;
\r
9421 frames[count] = *mid;
\r
9424 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9426 for (n = 0; n < factor; n++) {
\r
9427 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9428 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9430 fraction = fraction * 2;
\r
9436 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9438 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9440 EvalGraphSet( first, last, current, pvInfoList );
\r