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 if(name[0] == '~' && name[1] == '\\') { // [HGM] recognize ~ as HOMEPATH environment variable
\r
1227 installDir = getenv("HOMEPATH");
\r
1229 strcpy(fullname, installDir);
\r
1230 strcat(fullname, "\\");
\r
1231 strcat(fullname, name);
\r
1232 return strlen(fullname);
\r
1234 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1238 MyGetFullPathName(char *name, char *fullname)
\r
1241 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1246 { // [HGM] args: allows testing if main window is realized from back-end
\r
1247 return hwndMain != NULL;
\r
1251 PopUpStartupDialog()
\r
1255 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1256 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1257 FreeProcInstance(lpProc);
\r
1260 /*---------------------------------------------------------------------------*\
\r
1262 * GDI board drawing routines
\r
1264 \*---------------------------------------------------------------------------*/
\r
1266 /* [AS] Draw square using background texture */
\r
1267 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1272 return; /* Should never happen! */
\r
1275 SetGraphicsMode( dst, GM_ADVANCED );
\r
1282 /* X reflection */
\r
1287 x.eDx = (FLOAT) dw + dx - 1;
\r
1290 SetWorldTransform( dst, &x );
\r
1293 /* Y reflection */
\r
1299 x.eDy = (FLOAT) dh + dy - 1;
\r
1301 SetWorldTransform( dst, &x );
\r
1309 x.eDx = (FLOAT) dx;
\r
1310 x.eDy = (FLOAT) dy;
\r
1313 SetWorldTransform( dst, &x );
\r
1317 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1325 SetWorldTransform( dst, &x );
\r
1327 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1330 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1332 PM_WP = (int) WhitePawn,
\r
1333 PM_WN = (int) WhiteKnight,
\r
1334 PM_WB = (int) WhiteBishop,
\r
1335 PM_WR = (int) WhiteRook,
\r
1336 PM_WQ = (int) WhiteQueen,
\r
1337 PM_WF = (int) WhiteFerz,
\r
1338 PM_WW = (int) WhiteWazir,
\r
1339 PM_WE = (int) WhiteAlfil,
\r
1340 PM_WM = (int) WhiteMan,
\r
1341 PM_WO = (int) WhiteCannon,
\r
1342 PM_WU = (int) WhiteUnicorn,
\r
1343 PM_WH = (int) WhiteNightrider,
\r
1344 PM_WA = (int) WhiteAngel,
\r
1345 PM_WC = (int) WhiteMarshall,
\r
1346 PM_WAB = (int) WhiteCardinal,
\r
1347 PM_WD = (int) WhiteDragon,
\r
1348 PM_WL = (int) WhiteLance,
\r
1349 PM_WS = (int) WhiteCobra,
\r
1350 PM_WV = (int) WhiteFalcon,
\r
1351 PM_WSG = (int) WhiteSilver,
\r
1352 PM_WG = (int) WhiteGrasshopper,
\r
1353 PM_WK = (int) WhiteKing,
\r
1354 PM_BP = (int) BlackPawn,
\r
1355 PM_BN = (int) BlackKnight,
\r
1356 PM_BB = (int) BlackBishop,
\r
1357 PM_BR = (int) BlackRook,
\r
1358 PM_BQ = (int) BlackQueen,
\r
1359 PM_BF = (int) BlackFerz,
\r
1360 PM_BW = (int) BlackWazir,
\r
1361 PM_BE = (int) BlackAlfil,
\r
1362 PM_BM = (int) BlackMan,
\r
1363 PM_BO = (int) BlackCannon,
\r
1364 PM_BU = (int) BlackUnicorn,
\r
1365 PM_BH = (int) BlackNightrider,
\r
1366 PM_BA = (int) BlackAngel,
\r
1367 PM_BC = (int) BlackMarshall,
\r
1368 PM_BG = (int) BlackGrasshopper,
\r
1369 PM_BAB = (int) BlackCardinal,
\r
1370 PM_BD = (int) BlackDragon,
\r
1371 PM_BL = (int) BlackLance,
\r
1372 PM_BS = (int) BlackCobra,
\r
1373 PM_BV = (int) BlackFalcon,
\r
1374 PM_BSG = (int) BlackSilver,
\r
1375 PM_BK = (int) BlackKing
\r
1378 static HFONT hPieceFont = NULL;
\r
1379 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1380 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1381 static int fontBitmapSquareSize = 0;
\r
1382 static char pieceToFontChar[(int) EmptySquare] =
\r
1383 { 'p', 'n', 'b', 'r', 'q',
\r
1384 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1385 'k', 'o', 'm', 'v', 't', 'w',
\r
1386 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1389 extern BOOL SetCharTable( char *table, const char * map );
\r
1390 /* [HGM] moved to backend.c */
\r
1392 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1395 BYTE r1 = GetRValue( color );
\r
1396 BYTE g1 = GetGValue( color );
\r
1397 BYTE b1 = GetBValue( color );
\r
1403 /* Create a uniform background first */
\r
1404 hbrush = CreateSolidBrush( color );
\r
1405 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1406 FillRect( hdc, &rc, hbrush );
\r
1407 DeleteObject( hbrush );
\r
1410 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1411 int steps = squareSize / 2;
\r
1414 for( i=0; i<steps; i++ ) {
\r
1415 BYTE r = r1 - (r1-r2) * i / steps;
\r
1416 BYTE g = g1 - (g1-g2) * i / steps;
\r
1417 BYTE b = b1 - (b1-b2) * i / steps;
\r
1419 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1420 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1421 FillRect( hdc, &rc, hbrush );
\r
1422 DeleteObject(hbrush);
\r
1425 else if( mode == 2 ) {
\r
1426 /* Diagonal gradient, good more or less for every piece */
\r
1427 POINT triangle[3];
\r
1428 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1429 HBRUSH hbrush_old;
\r
1430 int steps = squareSize;
\r
1433 triangle[0].x = squareSize - steps;
\r
1434 triangle[0].y = squareSize;
\r
1435 triangle[1].x = squareSize;
\r
1436 triangle[1].y = squareSize;
\r
1437 triangle[2].x = squareSize;
\r
1438 triangle[2].y = squareSize - steps;
\r
1440 for( i=0; i<steps; i++ ) {
\r
1441 BYTE r = r1 - (r1-r2) * i / steps;
\r
1442 BYTE g = g1 - (g1-g2) * i / steps;
\r
1443 BYTE b = b1 - (b1-b2) * i / steps;
\r
1445 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1446 hbrush_old = SelectObject( hdc, hbrush );
\r
1447 Polygon( hdc, triangle, 3 );
\r
1448 SelectObject( hdc, hbrush_old );
\r
1449 DeleteObject(hbrush);
\r
1454 SelectObject( hdc, hpen );
\r
1459 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1460 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1461 piece: follow the steps as explained below.
\r
1463 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1467 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1471 int backColor = whitePieceColor;
\r
1472 int foreColor = blackPieceColor;
\r
1474 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1475 backColor = appData.fontBackColorWhite;
\r
1476 foreColor = appData.fontForeColorWhite;
\r
1478 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1479 backColor = appData.fontBackColorBlack;
\r
1480 foreColor = appData.fontForeColorBlack;
\r
1484 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1486 hbm_old = SelectObject( hdc, hbm );
\r
1490 rc.right = squareSize;
\r
1491 rc.bottom = squareSize;
\r
1493 /* Step 1: background is now black */
\r
1494 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1496 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1498 pt.x = (squareSize - sz.cx) / 2;
\r
1499 pt.y = (squareSize - sz.cy) / 2;
\r
1501 SetBkMode( hdc, TRANSPARENT );
\r
1502 SetTextColor( hdc, chroma );
\r
1503 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1504 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1506 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1507 /* Step 3: the area outside the piece is filled with white */
\r
1508 // FloodFill( hdc, 0, 0, chroma );
\r
1509 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1510 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1511 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1512 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1513 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1515 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1516 but if the start point is not inside the piece we're lost!
\r
1517 There should be a better way to do this... if we could create a region or path
\r
1518 from the fill operation we would be fine for example.
\r
1520 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1521 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1523 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1524 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1525 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1527 SelectObject( dc2, bm2 );
\r
1528 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1529 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1530 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1531 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1532 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1535 DeleteObject( bm2 );
\r
1538 SetTextColor( hdc, 0 );
\r
1540 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1541 draw the piece again in black for safety.
\r
1543 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1545 SelectObject( hdc, hbm_old );
\r
1547 if( hPieceMask[index] != NULL ) {
\r
1548 DeleteObject( hPieceMask[index] );
\r
1551 hPieceMask[index] = hbm;
\r
1554 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1556 SelectObject( hdc, hbm );
\r
1559 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1560 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1561 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1563 SelectObject( dc1, hPieceMask[index] );
\r
1564 SelectObject( dc2, bm2 );
\r
1565 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1566 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1569 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1570 the piece background and deletes (makes transparent) the rest.
\r
1571 Thanks to that mask, we are free to paint the background with the greates
\r
1572 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1573 We use this, to make gradients and give the pieces a "roundish" look.
\r
1575 SetPieceBackground( hdc, backColor, 2 );
\r
1576 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1580 DeleteObject( bm2 );
\r
1583 SetTextColor( hdc, foreColor );
\r
1584 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1586 SelectObject( hdc, hbm_old );
\r
1588 if( hPieceFace[index] != NULL ) {
\r
1589 DeleteObject( hPieceFace[index] );
\r
1592 hPieceFace[index] = hbm;
\r
1595 static int TranslatePieceToFontPiece( int piece )
\r
1625 case BlackMarshall:
\r
1629 case BlackNightrider:
\r
1635 case BlackUnicorn:
\r
1639 case BlackGrasshopper:
\r
1651 case BlackCardinal:
\r
1658 case WhiteMarshall:
\r
1662 case WhiteNightrider:
\r
1668 case WhiteUnicorn:
\r
1672 case WhiteGrasshopper:
\r
1684 case WhiteCardinal:
\r
1693 void CreatePiecesFromFont()
\r
1696 HDC hdc_window = NULL;
\r
1702 if( fontBitmapSquareSize < 0 ) {
\r
1703 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1707 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1708 fontBitmapSquareSize = -1;
\r
1712 if( fontBitmapSquareSize != squareSize ) {
\r
1713 hdc_window = GetDC( hwndMain );
\r
1714 hdc = CreateCompatibleDC( hdc_window );
\r
1716 if( hPieceFont != NULL ) {
\r
1717 DeleteObject( hPieceFont );
\r
1720 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1721 hPieceMask[i] = NULL;
\r
1722 hPieceFace[i] = NULL;
\r
1728 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1729 fontHeight = appData.fontPieceSize;
\r
1732 fontHeight = (fontHeight * squareSize) / 100;
\r
1734 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1736 lf.lfEscapement = 0;
\r
1737 lf.lfOrientation = 0;
\r
1738 lf.lfWeight = FW_NORMAL;
\r
1740 lf.lfUnderline = 0;
\r
1741 lf.lfStrikeOut = 0;
\r
1742 lf.lfCharSet = DEFAULT_CHARSET;
\r
1743 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1744 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1745 lf.lfQuality = PROOF_QUALITY;
\r
1746 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1747 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1748 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1750 hPieceFont = CreateFontIndirect( &lf );
\r
1752 if( hPieceFont == NULL ) {
\r
1753 fontBitmapSquareSize = -2;
\r
1756 /* Setup font-to-piece character table */
\r
1757 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1758 /* No (or wrong) global settings, try to detect the font */
\r
1759 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1761 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1763 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1764 /* DiagramTT* family */
\r
1765 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1767 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1768 /* Fairy symbols */
\r
1769 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1771 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1772 /* Good Companion (Some characters get warped as literal :-( */
\r
1773 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1774 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1775 SetCharTable(pieceToFontChar, s);
\r
1778 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1779 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1783 /* Create bitmaps */
\r
1784 hfont_old = SelectObject( hdc, hPieceFont );
\r
1785 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1786 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1787 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1789 SelectObject( hdc, hfont_old );
\r
1791 fontBitmapSquareSize = squareSize;
\r
1795 if( hdc != NULL ) {
\r
1799 if( hdc_window != NULL ) {
\r
1800 ReleaseDC( hwndMain, hdc_window );
\r
1805 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1809 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1810 if (gameInfo.event &&
\r
1811 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1812 strcmp(name, "k80s") == 0) {
\r
1813 strcpy(name, "tim");
\r
1815 return LoadBitmap(hinst, name);
\r
1819 /* Insert a color into the program's logical palette
\r
1820 structure. This code assumes the given color is
\r
1821 the result of the RGB or PALETTERGB macro, and it
\r
1822 knows how those macros work (which is documented).
\r
1825 InsertInPalette(COLORREF color)
\r
1827 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1829 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1830 DisplayFatalError("Too many colors", 0, 1);
\r
1831 pLogPal->palNumEntries--;
\r
1835 pe->peFlags = (char) 0;
\r
1836 pe->peRed = (char) (0xFF & color);
\r
1837 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1838 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1844 InitDrawingColors()
\r
1846 if (pLogPal == NULL) {
\r
1847 /* Allocate enough memory for a logical palette with
\r
1848 * PALETTESIZE entries and set the size and version fields
\r
1849 * of the logical palette structure.
\r
1851 pLogPal = (NPLOGPALETTE)
\r
1852 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1853 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1854 pLogPal->palVersion = 0x300;
\r
1856 pLogPal->palNumEntries = 0;
\r
1858 InsertInPalette(lightSquareColor);
\r
1859 InsertInPalette(darkSquareColor);
\r
1860 InsertInPalette(whitePieceColor);
\r
1861 InsertInPalette(blackPieceColor);
\r
1862 InsertInPalette(highlightSquareColor);
\r
1863 InsertInPalette(premoveHighlightColor);
\r
1865 /* create a logical color palette according the information
\r
1866 * in the LOGPALETTE structure.
\r
1868 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1870 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1871 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1872 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1873 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1874 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1875 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1876 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1877 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1878 /* [AS] Force rendering of the font-based pieces */
\r
1879 if( fontBitmapSquareSize > 0 ) {
\r
1880 fontBitmapSquareSize = 0;
\r
1886 BoardWidth(int boardSize, int n)
\r
1887 { /* [HGM] argument n added to allow different width and height */
\r
1888 int lineGap = sizeInfo[boardSize].lineGap;
\r
1890 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1891 lineGap = appData.overrideLineGap;
\r
1894 return (n + 1) * lineGap +
\r
1895 n * sizeInfo[boardSize].squareSize;
\r
1898 /* Respond to board resize by dragging edge */
\r
1900 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1902 BoardSize newSize = NUM_SIZES - 1;
\r
1903 static int recurse = 0;
\r
1904 if (IsIconic(hwndMain)) return;
\r
1905 if (recurse > 0) return;
\r
1907 while (newSize > 0) {
\r
1908 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1909 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1910 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1913 boardSize = newSize;
\r
1914 InitDrawingSizes(boardSize, flags);
\r
1919 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1922 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1924 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1925 ChessSquare piece;
\r
1926 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1928 SIZE clockSize, messageSize;
\r
1930 char buf[MSG_SIZ];
\r
1932 HMENU hmenu = GetMenu(hwndMain);
\r
1933 RECT crect, wrect, oldRect;
\r
1935 LOGBRUSH logbrush;
\r
1937 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1938 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1940 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1941 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1943 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1944 oldRect.top = wpMain.y;
\r
1945 oldRect.right = wpMain.x + wpMain.width;
\r
1946 oldRect.bottom = wpMain.y + wpMain.height;
\r
1948 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1949 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1950 squareSize = sizeInfo[boardSize].squareSize;
\r
1951 lineGap = sizeInfo[boardSize].lineGap;
\r
1952 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1954 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1955 lineGap = appData.overrideLineGap;
\r
1958 if (tinyLayout != oldTinyLayout) {
\r
1959 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1961 style &= ~WS_SYSMENU;
\r
1962 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1963 "&Minimize\tCtrl+F4");
\r
1965 style |= WS_SYSMENU;
\r
1966 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1968 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1970 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1971 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1972 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1974 DrawMenuBar(hwndMain);
\r
1977 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1978 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1980 /* Get text area sizes */
\r
1981 hdc = GetDC(hwndMain);
\r
1982 if (appData.clockMode) {
\r
1983 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1985 sprintf(buf, "White");
\r
1987 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1988 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1989 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1990 str = "We only care about the height here";
\r
1991 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1992 SelectObject(hdc, oldFont);
\r
1993 ReleaseDC(hwndMain, hdc);
\r
1995 /* Compute where everything goes */
\r
1996 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1997 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1998 logoHeight = 2*clockSize.cy;
\r
1999 leftLogoRect.left = OUTER_MARGIN;
\r
2000 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2001 leftLogoRect.top = OUTER_MARGIN;
\r
2002 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2004 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2005 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2006 rightLogoRect.top = OUTER_MARGIN;
\r
2007 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2010 whiteRect.left = leftLogoRect.right;
\r
2011 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2012 whiteRect.top = OUTER_MARGIN;
\r
2013 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2015 blackRect.right = rightLogoRect.left;
\r
2016 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2017 blackRect.top = whiteRect.top;
\r
2018 blackRect.bottom = whiteRect.bottom;
\r
2020 whiteRect.left = OUTER_MARGIN;
\r
2021 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2022 whiteRect.top = OUTER_MARGIN;
\r
2023 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2025 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2026 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2027 blackRect.top = whiteRect.top;
\r
2028 blackRect.bottom = whiteRect.bottom;
\r
2030 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2033 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2034 if (appData.showButtonBar) {
\r
2035 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2036 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2038 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2040 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2041 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2043 boardRect.left = OUTER_MARGIN;
\r
2044 boardRect.right = boardRect.left + boardWidth;
\r
2045 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2046 boardRect.bottom = boardRect.top + boardHeight;
\r
2048 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2049 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2050 oldBoardSize = boardSize;
\r
2051 oldTinyLayout = tinyLayout;
\r
2052 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2053 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2054 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2055 winW *= 1 + twoBoards;
\r
2056 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2057 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2058 wpMain.height = winH; // without disturbing window attachments
\r
2059 GetWindowRect(hwndMain, &wrect);
\r
2060 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2061 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2063 // [HGM] placement: let attached windows follow size change.
\r
2064 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2065 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2066 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2067 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2068 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2070 /* compensate if menu bar wrapped */
\r
2071 GetClientRect(hwndMain, &crect);
\r
2072 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2073 wpMain.height += offby;
\r
2075 case WMSZ_TOPLEFT:
\r
2076 SetWindowPos(hwndMain, NULL,
\r
2077 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2078 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2081 case WMSZ_TOPRIGHT:
\r
2083 SetWindowPos(hwndMain, NULL,
\r
2084 wrect.left, wrect.bottom - wpMain.height,
\r
2085 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2088 case WMSZ_BOTTOMLEFT:
\r
2090 SetWindowPos(hwndMain, NULL,
\r
2091 wrect.right - wpMain.width, wrect.top,
\r
2092 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2095 case WMSZ_BOTTOMRIGHT:
\r
2099 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2100 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2105 for (i = 0; i < N_BUTTONS; i++) {
\r
2106 if (buttonDesc[i].hwnd != NULL) {
\r
2107 DestroyWindow(buttonDesc[i].hwnd);
\r
2108 buttonDesc[i].hwnd = NULL;
\r
2110 if (appData.showButtonBar) {
\r
2111 buttonDesc[i].hwnd =
\r
2112 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2113 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2114 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2115 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2116 (HMENU) buttonDesc[i].id,
\r
2117 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2119 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2120 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2121 MAKELPARAM(FALSE, 0));
\r
2123 if (buttonDesc[i].id == IDM_Pause)
\r
2124 hwndPause = buttonDesc[i].hwnd;
\r
2125 buttonDesc[i].wndproc = (WNDPROC)
\r
2126 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2129 if (gridPen != NULL) DeleteObject(gridPen);
\r
2130 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2131 if (premovePen != NULL) DeleteObject(premovePen);
\r
2132 if (lineGap != 0) {
\r
2133 logbrush.lbStyle = BS_SOLID;
\r
2134 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2136 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2137 lineGap, &logbrush, 0, NULL);
\r
2138 logbrush.lbColor = highlightSquareColor;
\r
2140 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2141 lineGap, &logbrush, 0, NULL);
\r
2143 logbrush.lbColor = premoveHighlightColor;
\r
2145 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2146 lineGap, &logbrush, 0, NULL);
\r
2148 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2149 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2150 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2151 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2152 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2153 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2154 BOARD_WIDTH * (squareSize + lineGap);
\r
2155 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2157 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2158 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2159 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2160 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2161 lineGap / 2 + (i * (squareSize + lineGap));
\r
2162 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2163 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2164 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2168 /* [HGM] Licensing requirement */
\r
2170 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2173 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2175 GothicPopUp( "", VariantNormal);
\r
2178 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2180 /* Load piece bitmaps for this board size */
\r
2181 for (i=0; i<=2; i++) {
\r
2182 for (piece = WhitePawn;
\r
2183 (int) piece < (int) BlackPawn;
\r
2184 piece = (ChessSquare) ((int) piece + 1)) {
\r
2185 if (pieceBitmap[i][piece] != NULL)
\r
2186 DeleteObject(pieceBitmap[i][piece]);
\r
2190 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2191 // Orthodox Chess pieces
\r
2192 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2193 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2194 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2195 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2196 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2197 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2198 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2199 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2200 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2201 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2202 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2203 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2204 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2205 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2206 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2207 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2208 // in Shogi, Hijack the unused Queen for Lance
\r
2209 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2210 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2211 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2213 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2214 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2215 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2218 if(squareSize <= 72 && squareSize >= 33) {
\r
2219 /* A & C are available in most sizes now */
\r
2220 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2221 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2222 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2223 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2224 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2225 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2226 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2227 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2228 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2229 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2230 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2231 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2232 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2233 } else { // Smirf-like
\r
2234 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2235 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2236 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2238 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2239 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2240 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2241 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2242 } else { // WinBoard standard
\r
2243 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2244 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2245 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2250 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2251 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2252 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2253 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2254 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2255 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2256 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2257 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2258 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2259 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2260 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2261 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2262 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2263 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2264 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2265 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2266 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2267 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2268 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2269 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2270 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2271 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2272 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2273 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2274 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2275 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2276 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2277 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2278 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2279 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2280 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2282 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2283 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2284 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2285 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2286 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2287 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2288 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2289 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2290 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2291 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2292 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2293 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2294 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2296 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2297 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2298 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2299 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2300 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2301 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2302 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2303 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2304 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2305 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2306 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2307 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2310 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2311 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2312 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2313 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2314 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2315 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2316 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2317 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2318 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2319 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2320 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2321 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2322 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2323 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2324 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2328 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2329 /* special Shogi support in this size */
\r
2330 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2331 for (piece = WhitePawn;
\r
2332 (int) piece < (int) BlackPawn;
\r
2333 piece = (ChessSquare) ((int) piece + 1)) {
\r
2334 if (pieceBitmap[i][piece] != NULL)
\r
2335 DeleteObject(pieceBitmap[i][piece]);
\r
2338 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2342 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2344 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2345 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2346 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2347 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2348 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2349 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2350 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2351 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2352 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2356 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2358 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2359 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2360 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2361 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2362 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2363 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2364 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2365 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2366 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2370 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2372 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2373 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2374 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2375 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2376 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2377 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2378 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2379 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2385 PieceBitmap(ChessSquare p, int kind)
\r
2387 if ((int) p >= (int) BlackPawn)
\r
2388 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2390 return pieceBitmap[kind][(int) p];
\r
2393 /***************************************************************/
\r
2395 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2396 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2398 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2399 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2403 SquareToPos(int row, int column, int * x, int * y)
\r
2406 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2407 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2409 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2410 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2415 DrawCoordsOnDC(HDC hdc)
\r
2417 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
2418 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
2419 char str[2] = { NULLCHAR, NULLCHAR };
\r
2420 int oldMode, oldAlign, x, y, start, i;
\r
2424 if (!appData.showCoords)
\r
2427 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2429 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2430 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2431 oldAlign = GetTextAlign(hdc);
\r
2432 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2434 y = boardRect.top + lineGap;
\r
2435 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2437 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2438 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2439 str[0] = files[start + i];
\r
2440 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2441 y += squareSize + lineGap;
\r
2444 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2446 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2447 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2448 str[0] = ranks[start + i];
\r
2449 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2450 x += squareSize + lineGap;
\r
2453 SelectObject(hdc, oldBrush);
\r
2454 SetBkMode(hdc, oldMode);
\r
2455 SetTextAlign(hdc, oldAlign);
\r
2456 SelectObject(hdc, oldFont);
\r
2460 DrawGridOnDC(HDC hdc)
\r
2464 if (lineGap != 0) {
\r
2465 oldPen = SelectObject(hdc, gridPen);
\r
2466 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2467 SelectObject(hdc, oldPen);
\r
2471 #define HIGHLIGHT_PEN 0
\r
2472 #define PREMOVE_PEN 1
\r
2475 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2478 HPEN oldPen, hPen;
\r
2479 if (lineGap == 0) return;
\r
2481 x1 = boardRect.left +
\r
2482 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2483 y1 = boardRect.top +
\r
2484 lineGap/2 + y * (squareSize + lineGap);
\r
2486 x1 = boardRect.left +
\r
2487 lineGap/2 + x * (squareSize + lineGap);
\r
2488 y1 = boardRect.top +
\r
2489 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2491 hPen = pen ? premovePen : highlightPen;
\r
2492 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2493 MoveToEx(hdc, x1, y1, NULL);
\r
2494 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2495 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2496 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2497 LineTo(hdc, x1, y1);
\r
2498 SelectObject(hdc, oldPen);
\r
2502 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2505 for (i=0; i<2; i++) {
\r
2506 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2507 DrawHighlightOnDC(hdc, TRUE,
\r
2508 h->sq[i].x, h->sq[i].y,
\r
2513 /* Note: sqcolor is used only in monoMode */
\r
2514 /* Note that this code is largely duplicated in woptions.c,
\r
2515 function DrawSampleSquare, so that needs to be updated too */
\r
2517 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2519 HBITMAP oldBitmap;
\r
2523 if (appData.blindfold) return;
\r
2525 /* [AS] Use font-based pieces if needed */
\r
2526 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2527 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2528 CreatePiecesFromFont();
\r
2530 if( fontBitmapSquareSize == squareSize ) {
\r
2531 int index = TranslatePieceToFontPiece(piece);
\r
2533 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2537 squareSize, squareSize,
\r
2542 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2546 squareSize, squareSize,
\r
2555 if (appData.monoMode) {
\r
2556 SelectObject(tmphdc, PieceBitmap(piece,
\r
2557 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2558 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2559 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2561 tmpSize = squareSize;
\r
2563 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2564 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2565 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2566 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2567 x += (squareSize - minorSize)>>1;
\r
2568 y += squareSize - minorSize - 2;
\r
2569 tmpSize = minorSize;
\r
2571 if (color || appData.allWhite ) {
\r
2572 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2574 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2575 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2576 if(appData.upsideDown && color==flipView)
\r
2577 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2579 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2580 /* Use black for outline of white pieces */
\r
2581 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2582 if(appData.upsideDown && color==flipView)
\r
2583 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2585 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2587 /* Use square color for details of black pieces */
\r
2588 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2589 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2590 if(appData.upsideDown && !flipView)
\r
2591 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2593 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2595 SelectObject(hdc, oldBrush);
\r
2596 SelectObject(tmphdc, oldBitmap);
\r
2600 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2601 int GetBackTextureMode( int algo )
\r
2603 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2607 case BACK_TEXTURE_MODE_PLAIN:
\r
2608 result = 1; /* Always use identity map */
\r
2610 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2611 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2619 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2620 to handle redraws cleanly (as random numbers would always be different).
\r
2622 VOID RebuildTextureSquareInfo()
\r
2632 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2634 if( liteBackTexture != NULL ) {
\r
2635 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2636 lite_w = bi.bmWidth;
\r
2637 lite_h = bi.bmHeight;
\r
2641 if( darkBackTexture != NULL ) {
\r
2642 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2643 dark_w = bi.bmWidth;
\r
2644 dark_h = bi.bmHeight;
\r
2648 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2649 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2650 if( (col + row) & 1 ) {
\r
2652 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2653 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2654 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2655 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2660 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2661 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2662 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2663 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2670 /* [AS] Arrow highlighting support */
\r
2672 static int A_WIDTH = 5; /* Width of arrow body */
\r
2674 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2675 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2677 static double Sqr( double x )
\r
2682 static int Round( double x )
\r
2684 return (int) (x + 0.5);
\r
2687 /* Draw an arrow between two points using current settings */
\r
2688 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2691 double dx, dy, j, k, x, y;
\r
2693 if( d_x == s_x ) {
\r
2694 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2696 arrow[0].x = s_x + A_WIDTH;
\r
2699 arrow[1].x = s_x + A_WIDTH;
\r
2700 arrow[1].y = d_y - h;
\r
2702 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2703 arrow[2].y = d_y - h;
\r
2708 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2709 arrow[4].y = d_y - h;
\r
2711 arrow[5].x = s_x - A_WIDTH;
\r
2712 arrow[5].y = d_y - h;
\r
2714 arrow[6].x = s_x - A_WIDTH;
\r
2717 else if( d_y == s_y ) {
\r
2718 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2721 arrow[0].y = s_y + A_WIDTH;
\r
2723 arrow[1].x = d_x - w;
\r
2724 arrow[1].y = s_y + A_WIDTH;
\r
2726 arrow[2].x = d_x - w;
\r
2727 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2732 arrow[4].x = d_x - w;
\r
2733 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2735 arrow[5].x = d_x - w;
\r
2736 arrow[5].y = s_y - A_WIDTH;
\r
2739 arrow[6].y = s_y - A_WIDTH;
\r
2742 /* [AS] Needed a lot of paper for this! :-) */
\r
2743 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2744 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2746 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2748 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2753 arrow[0].x = Round(x - j);
\r
2754 arrow[0].y = Round(y + j*dx);
\r
2756 arrow[1].x = Round(x + j);
\r
2757 arrow[1].y = Round(y - j*dx);
\r
2760 x = (double) d_x - k;
\r
2761 y = (double) d_y - k*dy;
\r
2764 x = (double) d_x + k;
\r
2765 y = (double) d_y + k*dy;
\r
2768 arrow[2].x = Round(x + j);
\r
2769 arrow[2].y = Round(y - j*dx);
\r
2771 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2772 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2777 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2778 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2780 arrow[6].x = Round(x - j);
\r
2781 arrow[6].y = Round(y + j*dx);
\r
2784 Polygon( hdc, arrow, 7 );
\r
2787 /* [AS] Draw an arrow between two squares */
\r
2788 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2790 int s_x, s_y, d_x, d_y;
\r
2797 if( s_col == d_col && s_row == d_row ) {
\r
2801 /* Get source and destination points */
\r
2802 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2803 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2806 d_y += squareSize / 4;
\r
2808 else if( d_y < s_y ) {
\r
2809 d_y += 3 * squareSize / 4;
\r
2812 d_y += squareSize / 2;
\r
2816 d_x += squareSize / 4;
\r
2818 else if( d_x < s_x ) {
\r
2819 d_x += 3 * squareSize / 4;
\r
2822 d_x += squareSize / 2;
\r
2825 s_x += squareSize / 2;
\r
2826 s_y += squareSize / 2;
\r
2828 /* Adjust width */
\r
2829 A_WIDTH = squareSize / 14;
\r
2832 stLB.lbStyle = BS_SOLID;
\r
2833 stLB.lbColor = appData.highlightArrowColor;
\r
2836 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2837 holdpen = SelectObject( hdc, hpen );
\r
2838 hbrush = CreateBrushIndirect( &stLB );
\r
2839 holdbrush = SelectObject( hdc, hbrush );
\r
2841 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2843 SelectObject( hdc, holdpen );
\r
2844 SelectObject( hdc, holdbrush );
\r
2845 DeleteObject( hpen );
\r
2846 DeleteObject( hbrush );
\r
2849 BOOL HasHighlightInfo()
\r
2851 BOOL result = FALSE;
\r
2853 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2854 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2862 BOOL IsDrawArrowEnabled()
\r
2864 BOOL result = FALSE;
\r
2866 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2873 VOID DrawArrowHighlight( HDC hdc )
\r
2875 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2876 DrawArrowBetweenSquares( hdc,
\r
2877 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2878 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2882 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2884 HRGN result = NULL;
\r
2886 if( HasHighlightInfo() ) {
\r
2887 int x1, y1, x2, y2;
\r
2888 int sx, sy, dx, dy;
\r
2890 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2891 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2893 sx = MIN( x1, x2 );
\r
2894 sy = MIN( y1, y2 );
\r
2895 dx = MAX( x1, x2 ) + squareSize;
\r
2896 dy = MAX( y1, y2 ) + squareSize;
\r
2898 result = CreateRectRgn( sx, sy, dx, dy );
\r
2905 Warning: this function modifies the behavior of several other functions.
\r
2907 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2908 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2909 repaint is scattered all over the place, which is not good for features such as
\r
2910 "arrow highlighting" that require a full repaint of the board.
\r
2912 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2913 user interaction, when speed is not so important) but especially to avoid errors
\r
2914 in the displayed graphics.
\r
2916 In such patched places, I always try refer to this function so there is a single
\r
2917 place to maintain knowledge.
\r
2919 To restore the original behavior, just return FALSE unconditionally.
\r
2921 BOOL IsFullRepaintPreferrable()
\r
2923 BOOL result = FALSE;
\r
2925 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2926 /* Arrow may appear on the board */
\r
2934 This function is called by DrawPosition to know whether a full repaint must
\r
2937 Only DrawPosition may directly call this function, which makes use of
\r
2938 some state information. Other function should call DrawPosition specifying
\r
2939 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2941 BOOL DrawPositionNeedsFullRepaint()
\r
2943 BOOL result = FALSE;
\r
2946 Probably a slightly better policy would be to trigger a full repaint
\r
2947 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2948 but animation is fast enough that it's difficult to notice.
\r
2950 if( animInfo.piece == EmptySquare ) {
\r
2951 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2960 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2962 int row, column, x, y, square_color, piece_color;
\r
2963 ChessSquare piece;
\r
2965 HDC texture_hdc = NULL;
\r
2967 /* [AS] Initialize background textures if needed */
\r
2968 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2969 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2970 if( backTextureSquareSize != squareSize
\r
2971 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2972 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2973 backTextureSquareSize = squareSize;
\r
2974 RebuildTextureSquareInfo();
\r
2977 texture_hdc = CreateCompatibleDC( hdc );
\r
2980 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2981 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2983 SquareToPos(row, column, &x, &y);
\r
2985 piece = board[row][column];
\r
2987 square_color = ((column + row) % 2) == 1;
\r
2988 if( gameInfo.variant == VariantXiangqi ) {
\r
2989 square_color = !InPalace(row, column);
\r
2990 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2991 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2993 piece_color = (int) piece < (int) BlackPawn;
\r
2996 /* [HGM] holdings file: light square or black */
\r
2997 if(column == BOARD_LEFT-2) {
\r
2998 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3001 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3005 if(column == BOARD_RGHT + 1 ) {
\r
3006 if( row < gameInfo.holdingsSize )
\r
3009 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3013 if(column == BOARD_LEFT-1 ) /* left align */
\r
3014 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3015 else if( column == BOARD_RGHT) /* right align */
\r
3016 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3018 if (appData.monoMode) {
\r
3019 if (piece == EmptySquare) {
\r
3020 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3021 square_color ? WHITENESS : BLACKNESS);
\r
3023 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3026 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3027 /* [AS] Draw the square using a texture bitmap */
\r
3028 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3029 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3030 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3033 squareSize, squareSize,
\r
3036 backTextureSquareInfo[r][c].mode,
\r
3037 backTextureSquareInfo[r][c].x,
\r
3038 backTextureSquareInfo[r][c].y );
\r
3040 SelectObject( texture_hdc, hbm );
\r
3042 if (piece != EmptySquare) {
\r
3043 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3047 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3049 oldBrush = SelectObject(hdc, brush );
\r
3050 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3051 SelectObject(hdc, oldBrush);
\r
3052 if (piece != EmptySquare)
\r
3053 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3058 if( texture_hdc != NULL ) {
\r
3059 DeleteDC( texture_hdc );
\r
3063 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3064 void fputDW(FILE *f, int x)
\r
3066 fputc(x & 255, f);
\r
3067 fputc(x>>8 & 255, f);
\r
3068 fputc(x>>16 & 255, f);
\r
3069 fputc(x>>24 & 255, f);
\r
3072 #define MAX_CLIPS 200 /* more than enough */
\r
3075 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3077 // HBITMAP bufferBitmap;
\r
3082 int w = 100, h = 50;
\r
3084 if(logo == NULL) return;
\r
3085 // GetClientRect(hwndMain, &Rect);
\r
3086 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3087 // Rect.bottom-Rect.top+1);
\r
3088 tmphdc = CreateCompatibleDC(hdc);
\r
3089 hbm = SelectObject(tmphdc, logo);
\r
3090 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3094 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3095 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3096 SelectObject(tmphdc, hbm);
\r
3100 static HDC hdcSeek;
\r
3102 // [HGM] seekgraph
\r
3103 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3106 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3107 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3108 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3109 SelectObject( hdcSeek, hp );
\r
3112 // front-end wrapper for drawing functions to do rectangles
\r
3113 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3118 if (hdcSeek == NULL) {
\r
3119 hdcSeek = GetDC(hwndMain);
\r
3120 if (!appData.monoMode) {
\r
3121 SelectPalette(hdcSeek, hPal, FALSE);
\r
3122 RealizePalette(hdcSeek);
\r
3125 hp = SelectObject( hdcSeek, gridPen );
\r
3126 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3127 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3128 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3129 SelectObject( hdcSeek, hp );
\r
3132 // front-end wrapper for putting text in graph
\r
3133 void DrawSeekText(char *buf, int x, int y)
\r
3136 SetBkMode( hdcSeek, TRANSPARENT );
\r
3137 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3138 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3141 void DrawSeekDot(int x, int y, int color)
\r
3143 int square = color & 0x80;
\r
3145 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3146 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3148 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3149 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3151 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3152 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3153 SelectObject(hdcSeek, oldBrush);
\r
3157 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3159 static Board lastReq[2], lastDrawn[2];
\r
3160 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3161 static int lastDrawnFlipView = 0;
\r
3162 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3163 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3166 HBITMAP bufferBitmap;
\r
3167 HBITMAP oldBitmap;
\r
3169 HRGN clips[MAX_CLIPS];
\r
3170 ChessSquare dragged_piece = EmptySquare;
\r
3171 int nr = twoBoards*partnerUp;
\r
3173 /* I'm undecided on this - this function figures out whether a full
\r
3174 * repaint is necessary on its own, so there's no real reason to have the
\r
3175 * caller tell it that. I think this can safely be set to FALSE - but
\r
3176 * if we trust the callers not to request full repaints unnessesarily, then
\r
3177 * we could skip some clipping work. In other words, only request a full
\r
3178 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3179 * gamestart and similar) --Hawk
\r
3181 Boolean fullrepaint = repaint;
\r
3183 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3185 if( DrawPositionNeedsFullRepaint() ) {
\r
3186 fullrepaint = TRUE;
\r
3189 if (board == NULL) {
\r
3190 if (!lastReqValid[nr]) {
\r
3193 board = lastReq[nr];
\r
3195 CopyBoard(lastReq[nr], board);
\r
3196 lastReqValid[nr] = 1;
\r
3199 if (doingSizing) {
\r
3203 if (IsIconic(hwndMain)) {
\r
3207 if (hdc == NULL) {
\r
3208 hdc = GetDC(hwndMain);
\r
3209 if (!appData.monoMode) {
\r
3210 SelectPalette(hdc, hPal, FALSE);
\r
3211 RealizePalette(hdc);
\r
3215 releaseDC = FALSE;
\r
3218 /* Create some work-DCs */
\r
3219 hdcmem = CreateCompatibleDC(hdc);
\r
3220 tmphdc = CreateCompatibleDC(hdc);
\r
3222 /* If dragging is in progress, we temporarely remove the piece */
\r
3223 /* [HGM] or temporarily decrease count if stacked */
\r
3224 /* !! Moved to before board compare !! */
\r
3225 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3226 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3227 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3228 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3229 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3231 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3232 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3233 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3235 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3238 /* Figure out which squares need updating by comparing the
\r
3239 * newest board with the last drawn board and checking if
\r
3240 * flipping has changed.
\r
3242 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3243 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3244 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3245 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3246 SquareToPos(row, column, &x, &y);
\r
3247 clips[num_clips++] =
\r
3248 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3252 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3253 for (i=0; i<2; i++) {
\r
3254 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3255 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3256 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3257 lastDrawnHighlight.sq[i].y >= 0) {
\r
3258 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3259 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3260 clips[num_clips++] =
\r
3261 CreateRectRgn(x - lineGap, y - lineGap,
\r
3262 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3264 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3265 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3266 clips[num_clips++] =
\r
3267 CreateRectRgn(x - lineGap, y - lineGap,
\r
3268 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3272 for (i=0; i<2; i++) {
\r
3273 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3274 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3275 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3276 lastDrawnPremove.sq[i].y >= 0) {
\r
3277 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3278 lastDrawnPremove.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
3283 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3284 premoveHighlightInfo.sq[i].y >= 0) {
\r
3285 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3286 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3287 clips[num_clips++] =
\r
3288 CreateRectRgn(x - lineGap, y - lineGap,
\r
3289 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3293 } else { // nr == 1
\r
3294 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3295 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3296 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3297 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3298 for (i=0; i<2; i++) {
\r
3299 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3300 partnerHighlightInfo.sq[i].y >= 0) {
\r
3301 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3302 partnerHighlightInfo.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
3307 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3308 oldPartnerHighlight.sq[i].y >= 0) {
\r
3309 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3310 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3311 clips[num_clips++] =
\r
3312 CreateRectRgn(x - lineGap, y - lineGap,
\r
3313 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3318 fullrepaint = TRUE;
\r
3321 /* Create a buffer bitmap - this is the actual bitmap
\r
3322 * being written to. When all the work is done, we can
\r
3323 * copy it to the real DC (the screen). This avoids
\r
3324 * the problems with flickering.
\r
3326 GetClientRect(hwndMain, &Rect);
\r
3327 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3328 Rect.bottom-Rect.top+1);
\r
3329 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3330 if (!appData.monoMode) {
\r
3331 SelectPalette(hdcmem, hPal, FALSE);
\r
3334 /* Create clips for dragging */
\r
3335 if (!fullrepaint) {
\r
3336 if (dragInfo.from.x >= 0) {
\r
3337 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3338 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3340 if (dragInfo.start.x >= 0) {
\r
3341 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3342 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3344 if (dragInfo.pos.x >= 0) {
\r
3345 x = dragInfo.pos.x - squareSize / 2;
\r
3346 y = dragInfo.pos.y - squareSize / 2;
\r
3347 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3349 if (dragInfo.lastpos.x >= 0) {
\r
3350 x = dragInfo.lastpos.x - squareSize / 2;
\r
3351 y = dragInfo.lastpos.y - squareSize / 2;
\r
3352 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3356 /* Are we animating a move?
\r
3358 * - remove the piece from the board (temporarely)
\r
3359 * - calculate the clipping region
\r
3361 if (!fullrepaint) {
\r
3362 if (animInfo.piece != EmptySquare) {
\r
3363 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3364 x = boardRect.left + animInfo.lastpos.x;
\r
3365 y = boardRect.top + animInfo.lastpos.y;
\r
3366 x2 = boardRect.left + animInfo.pos.x;
\r
3367 y2 = boardRect.top + animInfo.pos.y;
\r
3368 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3369 /* Slight kludge. The real problem is that after AnimateMove is
\r
3370 done, the position on the screen does not match lastDrawn.
\r
3371 This currently causes trouble only on e.p. captures in
\r
3372 atomic, where the piece moves to an empty square and then
\r
3373 explodes. The old and new positions both had an empty square
\r
3374 at the destination, but animation has drawn a piece there and
\r
3375 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3376 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3380 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3381 if (num_clips == 0)
\r
3382 fullrepaint = TRUE;
\r
3384 /* Set clipping on the memory DC */
\r
3385 if (!fullrepaint) {
\r
3386 SelectClipRgn(hdcmem, clips[0]);
\r
3387 for (x = 1; x < num_clips; x++) {
\r
3388 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3389 abort(); // this should never ever happen!
\r
3393 /* Do all the drawing to the memory DC */
\r
3394 if(explodeInfo.radius) { // [HGM] atomic
\r
3396 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3397 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3398 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3399 x += squareSize/2;
\r
3400 y += squareSize/2;
\r
3401 if(!fullrepaint) {
\r
3402 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3403 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3405 DrawGridOnDC(hdcmem);
\r
3406 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3407 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3408 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3409 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3410 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3411 SelectObject(hdcmem, oldBrush);
\r
3413 DrawGridOnDC(hdcmem);
\r
3414 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3415 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3416 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3418 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3419 oldPartnerHighlight = partnerHighlightInfo;
\r
3421 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3423 if(nr == 0) // [HGM] dual: markers only on left board
\r
3424 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3425 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3426 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3427 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3428 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3429 SquareToPos(row, column, &x, &y);
\r
3430 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3431 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3432 SelectObject(hdcmem, oldBrush);
\r
3437 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3438 if(appData.autoLogo) {
\r
3440 switch(gameMode) { // pick logos based on game mode
\r
3441 case IcsObserving:
\r
3442 whiteLogo = second.programLogo; // ICS logo
\r
3443 blackLogo = second.programLogo;
\r
3446 case IcsPlayingWhite:
\r
3447 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3448 blackLogo = second.programLogo; // ICS logo
\r
3450 case IcsPlayingBlack:
\r
3451 whiteLogo = second.programLogo; // ICS logo
\r
3452 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3454 case TwoMachinesPlay:
\r
3455 if(first.twoMachinesColor[0] == 'b') {
\r
3456 whiteLogo = second.programLogo;
\r
3457 blackLogo = first.programLogo;
\r
3460 case MachinePlaysWhite:
\r
3461 blackLogo = userLogo;
\r
3463 case MachinePlaysBlack:
\r
3464 whiteLogo = userLogo;
\r
3465 blackLogo = first.programLogo;
\r
3468 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3469 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3472 if( appData.highlightMoveWithArrow ) {
\r
3473 DrawArrowHighlight(hdcmem);
\r
3476 DrawCoordsOnDC(hdcmem);
\r
3478 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3479 /* to make sure lastDrawn contains what is actually drawn */
\r
3481 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3482 if (dragged_piece != EmptySquare) {
\r
3483 /* [HGM] or restack */
\r
3484 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3485 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3487 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3488 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3489 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3490 x = dragInfo.pos.x - squareSize / 2;
\r
3491 y = dragInfo.pos.y - squareSize / 2;
\r
3492 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3493 ((int) dragged_piece < (int) BlackPawn),
\r
3494 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3497 /* Put the animated piece back into place and draw it */
\r
3498 if (animInfo.piece != EmptySquare) {
\r
3499 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3500 x = boardRect.left + animInfo.pos.x;
\r
3501 y = boardRect.top + animInfo.pos.y;
\r
3502 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3503 ((int) animInfo.piece < (int) BlackPawn),
\r
3504 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3507 /* Release the bufferBitmap by selecting in the old bitmap
\r
3508 * and delete the memory DC
\r
3510 SelectObject(hdcmem, oldBitmap);
\r
3513 /* Set clipping on the target DC */
\r
3514 if (!fullrepaint) {
\r
3515 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3517 GetRgnBox(clips[x], &rect);
\r
3518 DeleteObject(clips[x]);
\r
3519 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3520 rect.right + wpMain.width/2, rect.bottom);
\r
3522 SelectClipRgn(hdc, clips[0]);
\r
3523 for (x = 1; x < num_clips; x++) {
\r
3524 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3525 abort(); // this should never ever happen!
\r
3529 /* Copy the new bitmap onto the screen in one go.
\r
3530 * This way we avoid any flickering
\r
3532 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3533 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3534 boardRect.right - boardRect.left,
\r
3535 boardRect.bottom - boardRect.top,
\r
3536 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3537 if(saveDiagFlag) {
\r
3538 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3539 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3541 GetObject(bufferBitmap, sizeof(b), &b);
\r
3542 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3543 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3544 bih.biWidth = b.bmWidth;
\r
3545 bih.biHeight = b.bmHeight;
\r
3547 bih.biBitCount = b.bmBitsPixel;
\r
3548 bih.biCompression = 0;
\r
3549 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3550 bih.biXPelsPerMeter = 0;
\r
3551 bih.biYPelsPerMeter = 0;
\r
3552 bih.biClrUsed = 0;
\r
3553 bih.biClrImportant = 0;
\r
3554 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3555 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3556 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3557 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3559 wb = b.bmWidthBytes;
\r
3561 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3562 int k = ((int*) pData)[i];
\r
3563 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3564 if(j >= 16) break;
\r
3566 if(j >= nrColors) nrColors = j+1;
\r
3568 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3570 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3571 for(w=0; w<(wb>>2); w+=2) {
\r
3572 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3573 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3574 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3575 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3576 pData[p++] = m | j<<4;
\r
3578 while(p&3) pData[p++] = 0;
\r
3581 wb = ((wb+31)>>5)<<2;
\r
3583 // write BITMAPFILEHEADER
\r
3584 fprintf(diagFile, "BM");
\r
3585 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3586 fputDW(diagFile, 0);
\r
3587 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3588 // write BITMAPINFOHEADER
\r
3589 fputDW(diagFile, 40);
\r
3590 fputDW(diagFile, b.bmWidth);
\r
3591 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3592 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3593 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3594 fputDW(diagFile, 0);
\r
3595 fputDW(diagFile, 0);
\r
3596 fputDW(diagFile, 0);
\r
3597 fputDW(diagFile, 0);
\r
3598 fputDW(diagFile, 0);
\r
3599 fputDW(diagFile, 0);
\r
3600 // write color table
\r
3602 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3603 // write bitmap data
\r
3604 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3605 fputc(pData[i], diagFile);
\r
3609 SelectObject(tmphdc, oldBitmap);
\r
3611 /* Massive cleanup */
\r
3612 for (x = 0; x < num_clips; x++)
\r
3613 DeleteObject(clips[x]);
\r
3616 DeleteObject(bufferBitmap);
\r
3619 ReleaseDC(hwndMain, hdc);
\r
3621 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3623 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3625 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3628 /* CopyBoard(lastDrawn, board);*/
\r
3629 lastDrawnHighlight = highlightInfo;
\r
3630 lastDrawnPremove = premoveHighlightInfo;
\r
3631 lastDrawnFlipView = flipView;
\r
3632 lastDrawnValid[nr] = 1;
\r
3635 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3640 saveDiagFlag = 1; diagFile = f;
\r
3641 HDCDrawPosition(NULL, TRUE, NULL);
\r
3645 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3652 /*---------------------------------------------------------------------------*\
\r
3653 | CLIENT PAINT PROCEDURE
\r
3654 | This is the main event-handler for the WM_PAINT message.
\r
3656 \*---------------------------------------------------------------------------*/
\r
3658 PaintProc(HWND hwnd)
\r
3664 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3665 if (IsIconic(hwnd)) {
\r
3666 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3668 if (!appData.monoMode) {
\r
3669 SelectPalette(hdc, hPal, FALSE);
\r
3670 RealizePalette(hdc);
\r
3672 HDCDrawPosition(hdc, 1, NULL);
\r
3673 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3674 flipView = !flipView; partnerUp = !partnerUp;
\r
3675 HDCDrawPosition(hdc, 1, NULL);
\r
3676 flipView = !flipView; partnerUp = !partnerUp;
\r
3679 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3680 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3681 ETO_CLIPPED|ETO_OPAQUE,
\r
3682 &messageRect, messageText, strlen(messageText), NULL);
\r
3683 SelectObject(hdc, oldFont);
\r
3684 DisplayBothClocks();
\r
3686 EndPaint(hwnd,&ps);
\r
3694 * If the user selects on a border boundary, return -1; if off the board,
\r
3695 * return -2. Otherwise map the event coordinate to the square.
\r
3696 * The offset boardRect.left or boardRect.top must already have been
\r
3697 * subtracted from x.
\r
3699 int EventToSquare(x, limit)
\r
3707 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3709 x /= (squareSize + lineGap);
\r
3721 DropEnable dropEnables[] = {
\r
3722 { 'P', DP_Pawn, "Pawn" },
\r
3723 { 'N', DP_Knight, "Knight" },
\r
3724 { 'B', DP_Bishop, "Bishop" },
\r
3725 { 'R', DP_Rook, "Rook" },
\r
3726 { 'Q', DP_Queen, "Queen" },
\r
3730 SetupDropMenu(HMENU hmenu)
\r
3732 int i, count, enable;
\r
3734 extern char white_holding[], black_holding[];
\r
3735 char item[MSG_SIZ];
\r
3737 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3738 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3739 dropEnables[i].piece);
\r
3741 while (p && *p++ == dropEnables[i].piece) count++;
\r
3742 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3743 enable = count > 0 || !appData.testLegality
\r
3744 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3745 && !appData.icsActive);
\r
3746 ModifyMenu(hmenu, dropEnables[i].command,
\r
3747 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3748 dropEnables[i].command, item);
\r
3752 void DragPieceBegin(int x, int y)
\r
3754 dragInfo.lastpos.x = boardRect.left + x;
\r
3755 dragInfo.lastpos.y = boardRect.top + y;
\r
3756 dragInfo.from.x = fromX;
\r
3757 dragInfo.from.y = fromY;
\r
3758 dragInfo.start = dragInfo.from;
\r
3759 SetCapture(hwndMain);
\r
3762 void DragPieceEnd(int x, int y)
\r
3765 dragInfo.start.x = dragInfo.start.y = -1;
\r
3766 dragInfo.from = dragInfo.start;
\r
3767 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3770 /* Event handler for mouse messages */
\r
3772 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3776 static int recursive = 0;
\r
3778 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3781 if (message == WM_MBUTTONUP) {
\r
3782 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3783 to the middle button: we simulate pressing the left button too!
\r
3785 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3786 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3792 pt.x = LOWORD(lParam);
\r
3793 pt.y = HIWORD(lParam);
\r
3794 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3795 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3796 if (!flipView && y >= 0) {
\r
3797 y = BOARD_HEIGHT - 1 - y;
\r
3799 if (flipView && x >= 0) {
\r
3800 x = BOARD_WIDTH - 1 - x;
\r
3803 switch (message) {
\r
3804 case WM_LBUTTONDOWN:
\r
3805 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3806 if (gameMode == EditPosition) {
\r
3807 SetWhiteToPlayEvent();
\r
3808 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3809 AdjustClock(flipClock, -1);
\r
3810 } else if (gameMode == IcsPlayingBlack ||
\r
3811 gameMode == MachinePlaysWhite) {
\r
3814 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3815 if (gameMode == EditPosition) {
\r
3816 SetBlackToPlayEvent();
\r
3817 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3818 AdjustClock(!flipClock, -1);
\r
3819 } else if (gameMode == IcsPlayingWhite ||
\r
3820 gameMode == MachinePlaysBlack) {
\r
3824 dragInfo.start.x = dragInfo.start.y = -1;
\r
3825 dragInfo.from = dragInfo.start;
\r
3826 if(fromX == -1 && frozen) { // not sure where this is for
\r
3827 fromX = fromY = -1;
\r
3828 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3831 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3832 DrawPosition(TRUE, NULL);
\r
3835 case WM_LBUTTONUP:
\r
3836 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3837 DrawPosition(TRUE, NULL);
\r
3840 case WM_MOUSEMOVE:
\r
3841 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3842 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3843 if ((appData.animateDragging || appData.highlightDragging)
\r
3844 && (wParam & MK_LBUTTON)
\r
3845 && dragInfo.from.x >= 0)
\r
3847 BOOL full_repaint = FALSE;
\r
3849 if (appData.animateDragging) {
\r
3850 dragInfo.pos = pt;
\r
3852 if (appData.highlightDragging) {
\r
3853 SetHighlights(fromX, fromY, x, y);
\r
3854 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3855 full_repaint = TRUE;
\r
3859 DrawPosition( full_repaint, NULL);
\r
3861 dragInfo.lastpos = dragInfo.pos;
\r
3865 case WM_MOUSEWHEEL: // [DM]
\r
3866 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3867 /* Mouse Wheel is being rolled forward
\r
3868 * Play moves forward
\r
3870 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3871 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3872 /* Mouse Wheel is being rolled backward
\r
3873 * Play moves backward
\r
3875 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3876 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3880 case WM_MBUTTONUP:
\r
3881 case WM_RBUTTONUP:
\r
3883 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3886 case WM_MBUTTONDOWN:
\r
3887 case WM_RBUTTONDOWN:
\r
3890 fromX = fromY = -1;
\r
3891 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3892 dragInfo.start.x = dragInfo.start.y = -1;
\r
3893 dragInfo.from = dragInfo.start;
\r
3894 dragInfo.lastpos = dragInfo.pos;
\r
3895 if (appData.highlightDragging) {
\r
3896 ClearHighlights();
\r
3899 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3900 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3901 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
3902 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3903 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
3907 DrawPosition(TRUE, NULL);
\r
3909 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3912 if (message == WM_MBUTTONDOWN) {
\r
3913 buttonCount = 3; /* even if system didn't think so */
\r
3914 if (wParam & MK_SHIFT)
\r
3915 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3917 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3918 } else { /* message == WM_RBUTTONDOWN */
\r
3919 /* Just have one menu, on the right button. Windows users don't
\r
3920 think to try the middle one, and sometimes other software steals
\r
3921 it, or it doesn't really exist. */
\r
3922 if(gameInfo.variant != VariantShogi)
\r
3923 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3925 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3929 SetCapture(hwndMain);
3932 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3933 SetupDropMenu(hmenu);
\r
3934 MenuPopup(hwnd, pt, hmenu, -1);
\r
3944 /* Preprocess messages for buttons in main window */
\r
3946 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3948 int id = GetWindowLong(hwnd, GWL_ID);
\r
3951 for (i=0; i<N_BUTTONS; i++) {
\r
3952 if (buttonDesc[i].id == id) break;
\r
3954 if (i == N_BUTTONS) return 0;
\r
3955 switch (message) {
\r
3960 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3961 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3968 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3971 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3972 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3973 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3974 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3976 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3978 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3979 PopUpMoveDialog((char)wParam);
\r
3985 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3988 /* Process messages for Promotion dialog box */
\r
3990 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3994 switch (message) {
\r
3995 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3996 /* Center the dialog over the application window */
\r
3997 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3998 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3999 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4000 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4001 SW_SHOW : SW_HIDE);
\r
4002 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4003 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4004 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4005 PieceToChar(WhiteAngel) != '~') ||
\r
4006 (PieceToChar(BlackAngel) >= 'A' &&
\r
4007 PieceToChar(BlackAngel) != '~') ) ?
\r
4008 SW_SHOW : SW_HIDE);
\r
4009 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4010 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4011 PieceToChar(WhiteMarshall) != '~') ||
\r
4012 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4013 PieceToChar(BlackMarshall) != '~') ) ?
\r
4014 SW_SHOW : SW_HIDE);
\r
4015 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4016 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4017 gameInfo.variant != VariantShogi ?
\r
4018 SW_SHOW : SW_HIDE);
\r
4019 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4020 gameInfo.variant != VariantShogi ?
\r
4021 SW_SHOW : SW_HIDE);
\r
4022 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4023 gameInfo.variant == VariantShogi ?
\r
4024 SW_SHOW : SW_HIDE);
\r
4025 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4026 gameInfo.variant == VariantShogi ?
\r
4027 SW_SHOW : SW_HIDE);
\r
4028 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4029 gameInfo.variant == VariantSuper ?
\r
4030 SW_SHOW : SW_HIDE);
\r
4033 case WM_COMMAND: /* message: received a command */
\r
4034 switch (LOWORD(wParam)) {
\r
4036 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4037 ClearHighlights();
\r
4038 DrawPosition(FALSE, NULL);
\r
4041 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4044 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4047 promoChar = PieceToChar(BlackRook);
\r
4050 promoChar = PieceToChar(BlackBishop);
\r
4052 case PB_Chancellor:
\r
4053 promoChar = PieceToChar(BlackMarshall);
\r
4055 case PB_Archbishop:
\r
4056 promoChar = PieceToChar(BlackAngel);
\r
4059 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4064 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4065 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4066 only show the popup when we are already sure the move is valid or
\r
4067 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4068 will figure out it is a promotion from the promoChar. */
\r
4069 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4070 fromX = fromY = -1;
\r
4071 if (!appData.highlightLastMove) {
\r
4072 ClearHighlights();
\r
4073 DrawPosition(FALSE, NULL);
\r
4080 /* Pop up promotion dialog */
\r
4082 PromotionPopup(HWND hwnd)
\r
4086 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4087 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4088 hwnd, (DLGPROC)lpProc);
\r
4089 FreeProcInstance(lpProc);
\r
4095 DrawPosition(TRUE, NULL);
\r
4096 PromotionPopup(hwndMain);
\r
4099 /* Toggle ShowThinking */
\r
4101 ToggleShowThinking()
\r
4103 appData.showThinking = !appData.showThinking;
\r
4104 ShowThinkingEvent();
\r
4108 LoadGameDialog(HWND hwnd, char* title)
\r
4112 char fileTitle[MSG_SIZ];
\r
4113 f = OpenFileDialog(hwnd, "rb", "",
\r
4114 appData.oldSaveStyle ? "gam" : "pgn",
\r
4116 title, &number, fileTitle, NULL);
\r
4118 cmailMsgLoaded = FALSE;
\r
4119 if (number == 0) {
\r
4120 int error = GameListBuild(f);
\r
4122 DisplayError("Cannot build game list", error);
\r
4123 } else if (!ListEmpty(&gameList) &&
\r
4124 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4125 GameListPopUp(f, fileTitle);
\r
4128 GameListDestroy();
\r
4131 LoadGame(f, number, fileTitle, FALSE);
\r
4135 int get_term_width()
\r
4140 HFONT hfont, hold_font;
\r
4145 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4149 // get the text metrics
\r
4150 hdc = GetDC(hText);
\r
4151 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4152 if (consoleCF.dwEffects & CFE_BOLD)
\r
4153 lf.lfWeight = FW_BOLD;
\r
4154 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4155 lf.lfItalic = TRUE;
\r
4156 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4157 lf.lfStrikeOut = TRUE;
\r
4158 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4159 lf.lfUnderline = TRUE;
\r
4160 hfont = CreateFontIndirect(&lf);
\r
4161 hold_font = SelectObject(hdc, hfont);
\r
4162 GetTextMetrics(hdc, &tm);
\r
4163 SelectObject(hdc, hold_font);
\r
4164 DeleteObject(hfont);
\r
4165 ReleaseDC(hText, hdc);
\r
4167 // get the rectangle
\r
4168 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4170 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4173 void UpdateICSWidth(HWND hText)
\r
4175 LONG old_width, new_width;
\r
4177 new_width = get_term_width(hText, FALSE);
\r
4178 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4179 if (new_width != old_width)
\r
4181 ics_update_width(new_width);
\r
4182 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4187 ChangedConsoleFont()
\r
4190 CHARRANGE tmpsel, sel;
\r
4191 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4192 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4193 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4196 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4197 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4198 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4199 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4200 * size. This was undocumented in the version of MSVC++ that I had
\r
4201 * when I wrote the code, but is apparently documented now.
\r
4203 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4204 cfmt.bCharSet = f->lf.lfCharSet;
\r
4205 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4206 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4207 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4208 /* Why are the following seemingly needed too? */
\r
4209 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4210 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4211 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4213 tmpsel.cpMax = -1; /*999999?*/
\r
4214 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4215 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4216 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4217 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4219 paraf.cbSize = sizeof(paraf);
\r
4220 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4221 paraf.dxStartIndent = 0;
\r
4222 paraf.dxOffset = WRAP_INDENT;
\r
4223 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4224 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4225 UpdateICSWidth(hText);
\r
4228 /*---------------------------------------------------------------------------*\
\r
4230 * Window Proc for main window
\r
4232 \*---------------------------------------------------------------------------*/
\r
4234 /* Process messages for main window, etc. */
\r
4236 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4239 int wmId, wmEvent;
\r
4243 char fileTitle[MSG_SIZ];
\r
4244 char buf[MSG_SIZ];
\r
4245 static SnapData sd;
\r
4247 switch (message) {
\r
4249 case WM_PAINT: /* message: repaint portion of window */
\r
4253 case WM_ERASEBKGND:
\r
4254 if (IsIconic(hwnd)) {
\r
4255 /* Cheat; change the message */
\r
4256 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4258 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4262 case WM_LBUTTONDOWN:
\r
4263 case WM_MBUTTONDOWN:
\r
4264 case WM_RBUTTONDOWN:
\r
4265 case WM_LBUTTONUP:
\r
4266 case WM_MBUTTONUP:
\r
4267 case WM_RBUTTONUP:
\r
4268 case WM_MOUSEMOVE:
\r
4269 case WM_MOUSEWHEEL:
\r
4270 MouseEvent(hwnd, message, wParam, lParam);
\r
4273 JAWS_KB_NAVIGATION
\r
4277 JAWS_ALT_INTERCEPT
\r
4279 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4280 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4281 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4282 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4284 SendMessage(h, message, wParam, lParam);
\r
4285 } else if(lParam != KF_REPEAT) {
\r
4286 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4287 PopUpMoveDialog((char)wParam);
\r
4288 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4289 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4294 case WM_PALETTECHANGED:
\r
4295 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4297 HDC hdc = GetDC(hwndMain);
\r
4298 SelectPalette(hdc, hPal, TRUE);
\r
4299 nnew = RealizePalette(hdc);
\r
4301 paletteChanged = TRUE;
\r
4302 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4304 ReleaseDC(hwnd, hdc);
\r
4308 case WM_QUERYNEWPALETTE:
\r
4309 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4311 HDC hdc = GetDC(hwndMain);
\r
4312 paletteChanged = FALSE;
\r
4313 SelectPalette(hdc, hPal, FALSE);
\r
4314 nnew = RealizePalette(hdc);
\r
4316 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4318 ReleaseDC(hwnd, hdc);
\r
4323 case WM_COMMAND: /* message: command from application menu */
\r
4324 wmId = LOWORD(wParam);
\r
4325 wmEvent = HIWORD(wParam);
\r
4330 SAY("new game enter a move to play against the computer with white");
\r
4333 case IDM_NewGameFRC:
\r
4334 if( NewGameFRC() == 0 ) {
\r
4339 case IDM_NewVariant:
\r
4340 NewVariantPopup(hwnd);
\r
4343 case IDM_LoadGame:
\r
4344 LoadGameDialog(hwnd, "Load Game from File");
\r
4347 case IDM_LoadNextGame:
\r
4351 case IDM_LoadPrevGame:
\r
4355 case IDM_ReloadGame:
\r
4359 case IDM_LoadPosition:
\r
4360 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4361 Reset(FALSE, TRUE);
\r
4364 f = OpenFileDialog(hwnd, "rb", "",
\r
4365 appData.oldSaveStyle ? "pos" : "fen",
\r
4367 "Load Position from File", &number, fileTitle, NULL);
\r
4369 LoadPosition(f, number, fileTitle);
\r
4373 case IDM_LoadNextPosition:
\r
4374 ReloadPosition(1);
\r
4377 case IDM_LoadPrevPosition:
\r
4378 ReloadPosition(-1);
\r
4381 case IDM_ReloadPosition:
\r
4382 ReloadPosition(0);
\r
4385 case IDM_SaveGame:
\r
4386 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4387 f = OpenFileDialog(hwnd, "a", defName,
\r
4388 appData.oldSaveStyle ? "gam" : "pgn",
\r
4390 "Save Game to File", NULL, fileTitle, NULL);
\r
4392 SaveGame(f, 0, "");
\r
4396 case IDM_SavePosition:
\r
4397 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4398 f = OpenFileDialog(hwnd, "a", defName,
\r
4399 appData.oldSaveStyle ? "pos" : "fen",
\r
4401 "Save Position to File", NULL, fileTitle, NULL);
\r
4403 SavePosition(f, 0, "");
\r
4407 case IDM_SaveDiagram:
\r
4408 defName = "diagram";
\r
4409 f = OpenFileDialog(hwnd, "wb", defName,
\r
4412 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4418 case IDM_CopyGame:
\r
4419 CopyGameToClipboard();
\r
4422 case IDM_PasteGame:
\r
4423 PasteGameFromClipboard();
\r
4426 case IDM_CopyGameListToClipboard:
\r
4427 CopyGameListToClipboard();
\r
4430 /* [AS] Autodetect FEN or PGN data */
\r
4431 case IDM_PasteAny:
\r
4432 PasteGameOrFENFromClipboard();
\r
4435 /* [AS] Move history */
\r
4436 case IDM_ShowMoveHistory:
\r
4437 if( MoveHistoryIsUp() ) {
\r
4438 MoveHistoryPopDown();
\r
4441 MoveHistoryPopUp();
\r
4445 /* [AS] Eval graph */
\r
4446 case IDM_ShowEvalGraph:
\r
4447 if( EvalGraphIsUp() ) {
\r
4448 EvalGraphPopDown();
\r
4452 SetFocus(hwndMain);
\r
4456 /* [AS] Engine output */
\r
4457 case IDM_ShowEngineOutput:
\r
4458 if( EngineOutputIsUp() ) {
\r
4459 EngineOutputPopDown();
\r
4462 EngineOutputPopUp();
\r
4466 /* [AS] User adjudication */
\r
4467 case IDM_UserAdjudication_White:
\r
4468 UserAdjudicationEvent( +1 );
\r
4471 case IDM_UserAdjudication_Black:
\r
4472 UserAdjudicationEvent( -1 );
\r
4475 case IDM_UserAdjudication_Draw:
\r
4476 UserAdjudicationEvent( 0 );
\r
4479 /* [AS] Game list options dialog */
\r
4480 case IDM_GameListOptions:
\r
4481 GameListOptions();
\r
4488 case IDM_CopyPosition:
\r
4489 CopyFENToClipboard();
\r
4492 case IDM_PastePosition:
\r
4493 PasteFENFromClipboard();
\r
4496 case IDM_MailMove:
\r
4500 case IDM_ReloadCMailMsg:
\r
4501 Reset(TRUE, TRUE);
\r
4502 ReloadCmailMsgEvent(FALSE);
\r
4505 case IDM_Minimize:
\r
4506 ShowWindow(hwnd, SW_MINIMIZE);
\r
4513 case IDM_MachineWhite:
\r
4514 MachineWhiteEvent();
\r
4516 * refresh the tags dialog only if it's visible
\r
4518 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4520 tags = PGNTags(&gameInfo);
\r
4521 TagsPopUp(tags, CmailMsg());
\r
4524 SAY("computer starts playing white");
\r
4527 case IDM_MachineBlack:
\r
4528 MachineBlackEvent();
\r
4530 * refresh the tags dialog only if it's visible
\r
4532 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4534 tags = PGNTags(&gameInfo);
\r
4535 TagsPopUp(tags, CmailMsg());
\r
4538 SAY("computer starts playing black");
\r
4541 case IDM_TwoMachines:
\r
4542 TwoMachinesEvent();
\r
4544 * refresh the tags dialog only if it's visible
\r
4546 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4548 tags = PGNTags(&gameInfo);
\r
4549 TagsPopUp(tags, CmailMsg());
\r
4552 SAY("programs start playing each other");
\r
4555 case IDM_AnalysisMode:
\r
4556 if (!first.analysisSupport) {
\r
4557 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4558 DisplayError(buf, 0);
\r
4560 SAY("analyzing current position");
\r
4561 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4562 if (appData.icsActive) {
\r
4563 if (gameMode != IcsObserving) {
\r
4564 sprintf(buf, "You are not observing a game");
\r
4565 DisplayError(buf, 0);
\r
4566 /* secure check */
\r
4567 if (appData.icsEngineAnalyze) {
\r
4568 if (appData.debugMode)
\r
4569 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4570 ExitAnalyzeMode();
\r
4576 /* if enable, user want disable icsEngineAnalyze */
\r
4577 if (appData.icsEngineAnalyze) {
\r
4578 ExitAnalyzeMode();
\r
4582 appData.icsEngineAnalyze = TRUE;
\r
4583 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4586 if (!appData.showThinking) ToggleShowThinking();
\r
4587 AnalyzeModeEvent();
\r
4591 case IDM_AnalyzeFile:
\r
4592 if (!first.analysisSupport) {
\r
4593 char buf[MSG_SIZ];
\r
4594 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4595 DisplayError(buf, 0);
\r
4597 if (!appData.showThinking) ToggleShowThinking();
\r
4598 AnalyzeFileEvent();
\r
4599 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4600 AnalysisPeriodicEvent(1);
\r
4604 case IDM_IcsClient:
\r
4608 case IDM_EditGame:
\r
4613 case IDM_EditPosition:
\r
4614 EditPositionEvent();
\r
4615 SAY("to set up a position type a FEN");
\r
4618 case IDM_Training:
\r
4622 case IDM_ShowGameList:
\r
4623 ShowGameListProc();
\r
4626 case IDM_EditTags:
\r
4630 case IDM_EditComment:
\r
4631 if (commentUp && editComment) {
\r
4634 EditCommentEvent();
\r
4654 case IDM_CallFlag:
\r
4674 case IDM_StopObserving:
\r
4675 StopObservingEvent();
\r
4678 case IDM_StopExamining:
\r
4679 StopExaminingEvent();
\r
4683 UploadGameEvent();
\r
4686 case IDM_TypeInMove:
\r
4687 PopUpMoveDialog('\000');
\r
4690 case IDM_TypeInName:
\r
4691 PopUpNameDialog('\000');
\r
4694 case IDM_Backward:
\r
4696 SetFocus(hwndMain);
\r
4703 SetFocus(hwndMain);
\r
4708 SetFocus(hwndMain);
\r
4713 SetFocus(hwndMain);
\r
4717 RevertEvent(FALSE);
\r
4720 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4721 RevertEvent(TRUE);
\r
4724 case IDM_TruncateGame:
\r
4725 TruncateGameEvent();
\r
4732 case IDM_RetractMove:
\r
4733 RetractMoveEvent();
\r
4736 case IDM_FlipView:
\r
4737 flipView = !flipView;
\r
4738 DrawPosition(FALSE, NULL);
\r
4741 case IDM_FlipClock:
\r
4742 flipClock = !flipClock;
\r
4743 DisplayBothClocks();
\r
4744 DrawPosition(FALSE, NULL);
\r
4747 case IDM_MuteSounds:
\r
4748 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4749 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4750 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4753 case IDM_GeneralOptions:
\r
4754 GeneralOptionsPopup(hwnd);
\r
4755 DrawPosition(TRUE, NULL);
\r
4758 case IDM_BoardOptions:
\r
4759 BoardOptionsPopup(hwnd);
\r
4762 case IDM_EnginePlayOptions:
\r
4763 EnginePlayOptionsPopup(hwnd);
\r
4766 case IDM_Engine1Options:
\r
4767 EngineOptionsPopup(hwnd, &first);
\r
4770 case IDM_Engine2Options:
\r
4771 EngineOptionsPopup(hwnd, &second);
\r
4774 case IDM_OptionsUCI:
\r
4775 UciOptionsPopup(hwnd);
\r
4778 case IDM_IcsOptions:
\r
4779 IcsOptionsPopup(hwnd);
\r
4783 FontsOptionsPopup(hwnd);
\r
4787 SoundOptionsPopup(hwnd);
\r
4790 case IDM_CommPort:
\r
4791 CommPortOptionsPopup(hwnd);
\r
4794 case IDM_LoadOptions:
\r
4795 LoadOptionsPopup(hwnd);
\r
4798 case IDM_SaveOptions:
\r
4799 SaveOptionsPopup(hwnd);
\r
4802 case IDM_TimeControl:
\r
4803 TimeControlOptionsPopup(hwnd);
\r
4806 case IDM_SaveSettings:
\r
4807 SaveSettings(settingsFileName);
\r
4810 case IDM_SaveSettingsOnExit:
\r
4811 saveSettingsOnExit = !saveSettingsOnExit;
\r
4812 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4813 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4814 MF_CHECKED : MF_UNCHECKED));
\r
4825 case IDM_AboutGame:
\r
4830 appData.debugMode = !appData.debugMode;
\r
4831 if (appData.debugMode) {
\r
4832 char dir[MSG_SIZ];
\r
4833 GetCurrentDirectory(MSG_SIZ, dir);
\r
4834 SetCurrentDirectory(installDir);
\r
4835 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4836 SetCurrentDirectory(dir);
\r
4837 setbuf(debugFP, NULL);
\r
4844 case IDM_HELPCONTENTS:
\r
4845 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4846 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4847 MessageBox (GetFocus(),
\r
4848 "Unable to activate help",
\r
4849 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4853 case IDM_HELPSEARCH:
\r
4854 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4855 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4856 MessageBox (GetFocus(),
\r
4857 "Unable to activate help",
\r
4858 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4862 case IDM_HELPHELP:
\r
4863 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4864 MessageBox (GetFocus(),
\r
4865 "Unable to activate help",
\r
4866 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4871 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4873 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4874 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4875 FreeProcInstance(lpProc);
\r
4878 case IDM_DirectCommand1:
\r
4879 AskQuestionEvent("Direct Command",
\r
4880 "Send to chess program:", "", "1");
\r
4882 case IDM_DirectCommand2:
\r
4883 AskQuestionEvent("Direct Command",
\r
4884 "Send to second chess program:", "", "2");
\r
4887 case EP_WhitePawn:
\r
4888 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4889 fromX = fromY = -1;
\r
4892 case EP_WhiteKnight:
\r
4893 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4894 fromX = fromY = -1;
\r
4897 case EP_WhiteBishop:
\r
4898 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4899 fromX = fromY = -1;
\r
4902 case EP_WhiteRook:
\r
4903 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4904 fromX = fromY = -1;
\r
4907 case EP_WhiteQueen:
\r
4908 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4909 fromX = fromY = -1;
\r
4912 case EP_WhiteFerz:
\r
4913 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4914 fromX = fromY = -1;
\r
4917 case EP_WhiteWazir:
\r
4918 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4919 fromX = fromY = -1;
\r
4922 case EP_WhiteAlfil:
\r
4923 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4924 fromX = fromY = -1;
\r
4927 case EP_WhiteCannon:
\r
4928 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4929 fromX = fromY = -1;
\r
4932 case EP_WhiteCardinal:
\r
4933 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4934 fromX = fromY = -1;
\r
4937 case EP_WhiteMarshall:
\r
4938 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4939 fromX = fromY = -1;
\r
4942 case EP_WhiteKing:
\r
4943 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4944 fromX = fromY = -1;
\r
4947 case EP_BlackPawn:
\r
4948 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4949 fromX = fromY = -1;
\r
4952 case EP_BlackKnight:
\r
4953 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4954 fromX = fromY = -1;
\r
4957 case EP_BlackBishop:
\r
4958 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4959 fromX = fromY = -1;
\r
4962 case EP_BlackRook:
\r
4963 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4964 fromX = fromY = -1;
\r
4967 case EP_BlackQueen:
\r
4968 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4969 fromX = fromY = -1;
\r
4972 case EP_BlackFerz:
\r
4973 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4974 fromX = fromY = -1;
\r
4977 case EP_BlackWazir:
\r
4978 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4979 fromX = fromY = -1;
\r
4982 case EP_BlackAlfil:
\r
4983 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4984 fromX = fromY = -1;
\r
4987 case EP_BlackCannon:
\r
4988 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4989 fromX = fromY = -1;
\r
4992 case EP_BlackCardinal:
\r
4993 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4994 fromX = fromY = -1;
\r
4997 case EP_BlackMarshall:
\r
4998 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4999 fromX = fromY = -1;
\r
5002 case EP_BlackKing:
\r
5003 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5004 fromX = fromY = -1;
\r
5007 case EP_EmptySquare:
\r
5008 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5009 fromX = fromY = -1;
\r
5012 case EP_ClearBoard:
\r
5013 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5014 fromX = fromY = -1;
\r
5018 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5019 fromX = fromY = -1;
\r
5023 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5024 fromX = fromY = -1;
\r
5028 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5029 fromX = fromY = -1;
\r
5033 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5034 fromX = fromY = -1;
\r
5038 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5039 fromX = fromY = -1;
\r
5043 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5044 fromX = fromY = -1;
\r
5048 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5049 fromX = fromY = -1;
\r
5053 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5054 fromX = fromY = -1;
\r
5058 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5059 fromX = fromY = -1;
\r
5063 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5069 case CLOCK_TIMER_ID:
\r
5070 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5071 clockTimerEvent = 0;
\r
5072 DecrementClocks(); /* call into back end */
\r
5074 case LOAD_GAME_TIMER_ID:
\r
5075 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5076 loadGameTimerEvent = 0;
\r
5077 AutoPlayGameLoop(); /* call into back end */
\r
5079 case ANALYSIS_TIMER_ID:
\r
5080 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5081 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5082 AnalysisPeriodicEvent(0);
\r
5084 KillTimer(hwnd, analysisTimerEvent);
\r
5085 analysisTimerEvent = 0;
\r
5088 case DELAYED_TIMER_ID:
\r
5089 KillTimer(hwnd, delayedTimerEvent);
\r
5090 delayedTimerEvent = 0;
\r
5091 delayedTimerCallback();
\r
5096 case WM_USER_Input:
\r
5097 InputEvent(hwnd, message, wParam, lParam);
\r
5100 /* [AS] Also move "attached" child windows */
\r
5101 case WM_WINDOWPOSCHANGING:
\r
5103 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5104 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5106 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5107 /* Window is moving */
\r
5110 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5111 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5112 rcMain.right = wpMain.x + wpMain.width;
\r
5113 rcMain.top = wpMain.y;
\r
5114 rcMain.bottom = wpMain.y + wpMain.height;
\r
5116 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5117 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5118 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5119 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5120 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5121 wpMain.x = lpwp->x;
\r
5122 wpMain.y = lpwp->y;
\r
5127 /* [AS] Snapping */
\r
5128 case WM_ENTERSIZEMOVE:
\r
5129 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5130 if (hwnd == hwndMain) {
\r
5131 doingSizing = TRUE;
\r
5134 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5138 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5139 if (hwnd == hwndMain) {
\r
5140 lastSizing = wParam;
\r
5145 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5146 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5148 case WM_EXITSIZEMOVE:
\r
5149 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5150 if (hwnd == hwndMain) {
\r
5152 doingSizing = FALSE;
\r
5153 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5154 GetClientRect(hwnd, &client);
\r
5155 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5157 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5159 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5162 case WM_DESTROY: /* message: window being destroyed */
\r
5163 PostQuitMessage(0);
\r
5167 if (hwnd == hwndMain) {
\r
5172 default: /* Passes it on if unprocessed */
\r
5173 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5178 /*---------------------------------------------------------------------------*\
\r
5180 * Misc utility routines
\r
5182 \*---------------------------------------------------------------------------*/
\r
5185 * Decent random number generator, at least not as bad as Windows
\r
5186 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5188 unsigned int randstate;
\r
5193 randstate = randstate * 1664525 + 1013904223;
\r
5194 return (int) randstate & 0x7fffffff;
\r
5198 mysrandom(unsigned int seed)
\r
5205 * returns TRUE if user selects a different color, FALSE otherwise
\r
5209 ChangeColor(HWND hwnd, COLORREF *which)
\r
5211 static BOOL firstTime = TRUE;
\r
5212 static DWORD customColors[16];
\r
5214 COLORREF newcolor;
\r
5219 /* Make initial colors in use available as custom colors */
\r
5220 /* Should we put the compiled-in defaults here instead? */
\r
5222 customColors[i++] = lightSquareColor & 0xffffff;
\r
5223 customColors[i++] = darkSquareColor & 0xffffff;
\r
5224 customColors[i++] = whitePieceColor & 0xffffff;
\r
5225 customColors[i++] = blackPieceColor & 0xffffff;
\r
5226 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5227 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5229 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5230 customColors[i++] = textAttribs[ccl].color;
\r
5232 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5233 firstTime = FALSE;
\r
5236 cc.lStructSize = sizeof(cc);
\r
5237 cc.hwndOwner = hwnd;
\r
5238 cc.hInstance = NULL;
\r
5239 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5240 cc.lpCustColors = (LPDWORD) customColors;
\r
5241 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5243 if (!ChooseColor(&cc)) return FALSE;
\r
5245 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5246 if (newcolor == *which) return FALSE;
\r
5247 *which = newcolor;
\r
5251 InitDrawingColors();
\r
5252 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5257 MyLoadSound(MySound *ms)
\r
5263 if (ms->data) free(ms->data);
\r
5266 switch (ms->name[0]) {
\r
5272 /* System sound from Control Panel. Don't preload here. */
\r
5276 if (ms->name[1] == NULLCHAR) {
\r
5277 /* "!" alone = silence */
\r
5280 /* Builtin wave resource. Error if not found. */
\r
5281 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5282 if (h == NULL) break;
\r
5283 ms->data = (void *)LoadResource(hInst, h);
\r
5284 if (h == NULL) break;
\r
5289 /* .wav file. Error if not found. */
\r
5290 f = fopen(ms->name, "rb");
\r
5291 if (f == NULL) break;
\r
5292 if (fstat(fileno(f), &st) < 0) break;
\r
5293 ms->data = malloc(st.st_size);
\r
5294 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5300 char buf[MSG_SIZ];
\r
5301 sprintf(buf, "Error loading sound %s", ms->name);
\r
5302 DisplayError(buf, GetLastError());
\r
5308 MyPlaySound(MySound *ms)
\r
5310 BOOLEAN ok = FALSE;
\r
5312 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5313 switch (ms->name[0]) {
\r
5315 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5320 /* System sound from Control Panel (deprecated feature).
\r
5321 "$" alone or an unset sound name gets default beep (still in use). */
\r
5322 if (ms->name[1]) {
\r
5323 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5325 if (!ok) ok = MessageBeep(MB_OK);
\r
5328 /* Builtin wave resource, or "!" alone for silence */
\r
5329 if (ms->name[1]) {
\r
5330 if (ms->data == NULL) return FALSE;
\r
5331 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5337 /* .wav file. Error if not found. */
\r
5338 if (ms->data == NULL) return FALSE;
\r
5339 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5342 /* Don't print an error: this can happen innocently if the sound driver
\r
5343 is busy; for instance, if another instance of WinBoard is playing
\r
5344 a sound at about the same time. */
\r
5350 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5353 OPENFILENAME *ofn;
\r
5354 static UINT *number; /* gross that this is static */
\r
5356 switch (message) {
\r
5357 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5358 /* Center the dialog over the application window */
\r
5359 ofn = (OPENFILENAME *) lParam;
\r
5360 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5361 number = (UINT *) ofn->lCustData;
\r
5362 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5366 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5367 return FALSE; /* Allow for further processing */
\r
5370 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5371 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5373 return FALSE; /* Allow for further processing */
\r
5379 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5381 static UINT *number;
\r
5382 OPENFILENAME *ofname;
\r
5385 case WM_INITDIALOG:
\r
5386 ofname = (OPENFILENAME *)lParam;
\r
5387 number = (UINT *)(ofname->lCustData);
\r
5390 ofnot = (OFNOTIFY *)lParam;
\r
5391 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5392 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5401 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5402 char *nameFilt, char *dlgTitle, UINT *number,
\r
5403 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5405 OPENFILENAME openFileName;
\r
5406 char buf1[MSG_SIZ];
\r
5409 if (fileName == NULL) fileName = buf1;
\r
5410 if (defName == NULL) {
\r
5411 strcpy(fileName, "*.");
\r
5412 strcat(fileName, defExt);
\r
5414 strcpy(fileName, defName);
\r
5416 if (fileTitle) strcpy(fileTitle, "");
\r
5417 if (number) *number = 0;
\r
5419 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5420 openFileName.hwndOwner = hwnd;
\r
5421 openFileName.hInstance = (HANDLE) hInst;
\r
5422 openFileName.lpstrFilter = nameFilt;
\r
5423 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5424 openFileName.nMaxCustFilter = 0L;
\r
5425 openFileName.nFilterIndex = 1L;
\r
5426 openFileName.lpstrFile = fileName;
\r
5427 openFileName.nMaxFile = MSG_SIZ;
\r
5428 openFileName.lpstrFileTitle = fileTitle;
\r
5429 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5430 openFileName.lpstrInitialDir = NULL;
\r
5431 openFileName.lpstrTitle = dlgTitle;
\r
5432 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5433 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5434 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5435 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5436 openFileName.nFileOffset = 0;
\r
5437 openFileName.nFileExtension = 0;
\r
5438 openFileName.lpstrDefExt = defExt;
\r
5439 openFileName.lCustData = (LONG) number;
\r
5440 openFileName.lpfnHook = oldDialog ?
\r
5441 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5442 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5444 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5445 GetOpenFileName(&openFileName)) {
\r
5446 /* open the file */
\r
5447 f = fopen(openFileName.lpstrFile, write);
\r
5449 MessageBox(hwnd, "File open failed", NULL,
\r
5450 MB_OK|MB_ICONEXCLAMATION);
\r
5454 int err = CommDlgExtendedError();
\r
5455 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5464 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5466 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5469 * Get the first pop-up menu in the menu template. This is the
\r
5470 * menu that TrackPopupMenu displays.
\r
5472 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5474 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5477 * TrackPopup uses screen coordinates, so convert the
\r
5478 * coordinates of the mouse click to screen coordinates.
\r
5480 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5482 /* Draw and track the floating pop-up menu. */
\r
5483 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5484 pt.x, pt.y, 0, hwnd, NULL);
\r
5486 /* Destroy the menu.*/
\r
5487 DestroyMenu(hmenu);
\r
5492 int sizeX, sizeY, newSizeX, newSizeY;
\r
5494 } ResizeEditPlusButtonsClosure;
\r
5497 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5499 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5503 if (hChild == cl->hText) return TRUE;
\r
5504 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5505 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5506 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5507 ScreenToClient(cl->hDlg, &pt);
\r
5508 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5509 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5513 /* Resize a dialog that has a (rich) edit field filling most of
\r
5514 the top, with a row of buttons below */
\r
5516 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5519 int newTextHeight, newTextWidth;
\r
5520 ResizeEditPlusButtonsClosure cl;
\r
5522 /*if (IsIconic(hDlg)) return;*/
\r
5523 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5525 cl.hdwp = BeginDeferWindowPos(8);
\r
5527 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5528 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5529 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5530 if (newTextHeight < 0) {
\r
5531 newSizeY += -newTextHeight;
\r
5532 newTextHeight = 0;
\r
5534 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5535 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5541 cl.newSizeX = newSizeX;
\r
5542 cl.newSizeY = newSizeY;
\r
5543 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5545 EndDeferWindowPos(cl.hdwp);
\r
5548 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5550 RECT rChild, rParent;
\r
5551 int wChild, hChild, wParent, hParent;
\r
5552 int wScreen, hScreen, xNew, yNew;
\r
5555 /* Get the Height and Width of the child window */
\r
5556 GetWindowRect (hwndChild, &rChild);
\r
5557 wChild = rChild.right - rChild.left;
\r
5558 hChild = rChild.bottom - rChild.top;
\r
5560 /* Get the Height and Width of the parent window */
\r
5561 GetWindowRect (hwndParent, &rParent);
\r
5562 wParent = rParent.right - rParent.left;
\r
5563 hParent = rParent.bottom - rParent.top;
\r
5565 /* Get the display limits */
\r
5566 hdc = GetDC (hwndChild);
\r
5567 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5568 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5569 ReleaseDC(hwndChild, hdc);
\r
5571 /* Calculate new X position, then adjust for screen */
\r
5572 xNew = rParent.left + ((wParent - wChild) /2);
\r
5575 } else if ((xNew+wChild) > wScreen) {
\r
5576 xNew = wScreen - wChild;
\r
5579 /* Calculate new Y position, then adjust for screen */
\r
5581 yNew = rParent.top + ((hParent - hChild) /2);
\r
5584 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5589 } else if ((yNew+hChild) > hScreen) {
\r
5590 yNew = hScreen - hChild;
\r
5593 /* Set it, and return */
\r
5594 return SetWindowPos (hwndChild, NULL,
\r
5595 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5598 /* Center one window over another */
\r
5599 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5601 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5604 /*---------------------------------------------------------------------------*\
\r
5606 * Startup Dialog functions
\r
5608 \*---------------------------------------------------------------------------*/
\r
5610 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5612 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5614 while (*cd != NULL) {
\r
5615 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5621 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5623 char buf1[MAX_ARG_LEN];
\r
5626 if (str[0] == '@') {
\r
5627 FILE* f = fopen(str + 1, "r");
\r
5629 DisplayFatalError(str + 1, errno, 2);
\r
5632 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5634 buf1[len] = NULLCHAR;
\r
5638 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5641 char buf[MSG_SIZ];
\r
5642 char *end = strchr(str, '\n');
\r
5643 if (end == NULL) return;
\r
5644 memcpy(buf, str, end - str);
\r
5645 buf[end - str] = NULLCHAR;
\r
5646 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5652 SetStartupDialogEnables(HWND hDlg)
\r
5654 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5655 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5656 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5657 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5658 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5659 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5660 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5661 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5662 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5663 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5664 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5665 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5666 IsDlgButtonChecked(hDlg, OPT_View));
\r
5670 QuoteForFilename(char *filename)
\r
5672 int dquote, space;
\r
5673 dquote = strchr(filename, '"') != NULL;
\r
5674 space = strchr(filename, ' ') != NULL;
\r
5675 if (dquote || space) {
\r
5687 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5689 char buf[MSG_SIZ];
\r
5692 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5693 q = QuoteForFilename(nthcp);
\r
5694 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5695 if (*nthdir != NULLCHAR) {
\r
5696 q = QuoteForFilename(nthdir);
\r
5697 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5699 if (*nthcp == NULLCHAR) {
\r
5700 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5701 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5702 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5703 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5708 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5710 char buf[MSG_SIZ];
\r
5714 switch (message) {
\r
5715 case WM_INITDIALOG:
\r
5716 /* Center the dialog */
\r
5717 CenterWindow (hDlg, GetDesktopWindow());
\r
5718 /* Initialize the dialog items */
\r
5719 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5720 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5721 firstChessProgramNames);
\r
5722 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5723 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5724 secondChessProgramNames);
\r
5725 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5726 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5727 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5728 if (*appData.icsHelper != NULLCHAR) {
\r
5729 char *q = QuoteForFilename(appData.icsHelper);
\r
5730 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5732 if (*appData.icsHost == NULLCHAR) {
\r
5733 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5734 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5735 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5736 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5737 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5740 if (appData.icsActive) {
\r
5741 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5743 else if (appData.noChessProgram) {
\r
5744 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5747 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5750 SetStartupDialogEnables(hDlg);
\r
5754 switch (LOWORD(wParam)) {
\r
5756 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5757 strcpy(buf, "/fcp=");
\r
5758 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5760 ParseArgs(StringGet, &p);
\r
5761 strcpy(buf, "/scp=");
\r
5762 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5764 ParseArgs(StringGet, &p);
\r
5765 appData.noChessProgram = FALSE;
\r
5766 appData.icsActive = FALSE;
\r
5767 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5768 strcpy(buf, "/ics /icshost=");
\r
5769 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5771 ParseArgs(StringGet, &p);
\r
5772 if (appData.zippyPlay) {
\r
5773 strcpy(buf, "/fcp=");
\r
5774 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5776 ParseArgs(StringGet, &p);
\r
5778 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5779 appData.noChessProgram = TRUE;
\r
5780 appData.icsActive = FALSE;
\r
5782 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5783 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5786 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5787 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5789 ParseArgs(StringGet, &p);
\r
5791 EndDialog(hDlg, TRUE);
\r
5798 case IDM_HELPCONTENTS:
\r
5799 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5800 MessageBox (GetFocus(),
\r
5801 "Unable to activate help",
\r
5802 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5807 SetStartupDialogEnables(hDlg);
\r
5815 /*---------------------------------------------------------------------------*\
\r
5817 * About box dialog functions
\r
5819 \*---------------------------------------------------------------------------*/
\r
5821 /* Process messages for "About" dialog box */
\r
5823 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5825 switch (message) {
\r
5826 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5827 /* Center the dialog over the application window */
\r
5828 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5829 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5833 case WM_COMMAND: /* message: received a command */
\r
5834 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5835 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5836 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5844 /*---------------------------------------------------------------------------*\
\r
5846 * Comment Dialog functions
\r
5848 \*---------------------------------------------------------------------------*/
\r
5851 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5853 static HANDLE hwndText = NULL;
\r
5854 int len, newSizeX, newSizeY, flags;
\r
5855 static int sizeX, sizeY;
\r
5860 switch (message) {
\r
5861 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5862 /* Initialize the dialog items */
\r
5863 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5864 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5865 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5866 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5867 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5868 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5869 SetWindowText(hDlg, commentTitle);
\r
5870 if (editComment) {
\r
5871 SetFocus(hwndText);
\r
5873 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5875 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5876 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5877 MAKELPARAM(FALSE, 0));
\r
5878 /* Size and position the dialog */
\r
5879 if (!commentDialog) {
\r
5880 commentDialog = hDlg;
\r
5881 flags = SWP_NOZORDER;
\r
5882 GetClientRect(hDlg, &rect);
\r
5883 sizeX = rect.right;
\r
5884 sizeY = rect.bottom;
\r
5885 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5886 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5887 WINDOWPLACEMENT wp;
\r
5888 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5889 wp.length = sizeof(WINDOWPLACEMENT);
\r
5891 wp.showCmd = SW_SHOW;
\r
5892 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5893 wp.rcNormalPosition.left = wpComment.x;
\r
5894 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5895 wp.rcNormalPosition.top = wpComment.y;
\r
5896 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5897 SetWindowPlacement(hDlg, &wp);
\r
5899 GetClientRect(hDlg, &rect);
\r
5900 newSizeX = rect.right;
\r
5901 newSizeY = rect.bottom;
\r
5902 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5903 newSizeX, newSizeY);
\r
5908 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5911 case WM_COMMAND: /* message: received a command */
\r
5912 switch (LOWORD(wParam)) {
\r
5914 if (editComment) {
\r
5916 /* Read changed options from the dialog box */
\r
5917 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5918 len = GetWindowTextLength(hwndText);
\r
5919 str = (char *) malloc(len + 1);
\r
5920 GetWindowText(hwndText, str, len + 1);
\r
5929 ReplaceComment(commentIndex, str);
\r
5936 case OPT_CancelComment:
\r
5940 case OPT_ClearComment:
\r
5941 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5944 case OPT_EditComment:
\r
5945 EditCommentEvent();
\r
5953 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5954 if( wParam == OPT_CommentText ) {
\r
5955 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5957 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5961 pt.x = LOWORD( lpMF->lParam );
\r
5962 pt.y = HIWORD( lpMF->lParam );
\r
5964 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5966 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5967 len = GetWindowTextLength(hwndText);
\r
5968 str = (char *) malloc(len + 1);
\r
5969 GetWindowText(hwndText, str, len + 1);
\r
5970 ReplaceComment(commentIndex, str);
\r
5971 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5972 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5975 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5976 lpMF->msg = WM_USER;
\r
5984 newSizeX = LOWORD(lParam);
\r
5985 newSizeY = HIWORD(lParam);
\r
5986 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5991 case WM_GETMINMAXINFO:
\r
5992 /* Prevent resizing window too small */
\r
5993 mmi = (MINMAXINFO *) lParam;
\r
5994 mmi->ptMinTrackSize.x = 100;
\r
5995 mmi->ptMinTrackSize.y = 100;
\r
6002 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6007 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6009 if (str == NULL) str = "";
\r
6010 p = (char *) malloc(2 * strlen(str) + 2);
\r
6013 if (*str == '\n') *q++ = '\r';
\r
6017 if (commentText != NULL) free(commentText);
\r
6019 commentIndex = index;
\r
6020 commentTitle = title;
\r
6022 editComment = edit;
\r
6024 if (commentDialog) {
\r
6025 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6026 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6028 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6029 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6030 hwndMain, (DLGPROC)lpProc);
\r
6031 FreeProcInstance(lpProc);
\r
6037 /*---------------------------------------------------------------------------*\
\r
6039 * Type-in move dialog functions
\r
6041 \*---------------------------------------------------------------------------*/
\r
6044 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6046 char move[MSG_SIZ];
\r
6048 ChessMove moveType;
\r
6049 int fromX, fromY, toX, toY;
\r
6052 switch (message) {
\r
6053 case WM_INITDIALOG:
\r
6054 move[0] = (char) lParam;
\r
6055 move[1] = NULLCHAR;
\r
6056 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6057 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6058 SetWindowText(hInput, move);
\r
6060 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6064 switch (LOWORD(wParam)) {
\r
6066 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6067 { int n; Board board;
\r
6069 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6070 EditPositionPasteFEN(move);
\r
6071 EndDialog(hDlg, TRUE);
\r
6074 // [HGM] movenum: allow move number to be typed in any mode
\r
6075 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6077 EndDialog(hDlg, TRUE);
\r
6081 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6082 gameMode != Training) {
\r
6083 DisplayMoveError("Displayed move is not current");
\r
6085 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6086 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6087 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6088 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6089 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6090 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6091 if (gameMode != Training)
\r
6092 forwardMostMove = currentMove;
\r
6093 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6095 DisplayMoveError("Could not parse move");
\r
6098 EndDialog(hDlg, TRUE);
\r
6101 EndDialog(hDlg, FALSE);
\r
6112 PopUpMoveDialog(char firstchar)
\r
6116 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6117 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6118 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6119 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6120 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6121 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6122 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6123 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6124 gameMode == Training) {
\r
6125 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6126 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6127 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6128 FreeProcInstance(lpProc);
\r
6132 /*---------------------------------------------------------------------------*\
\r
6134 * Type-in name dialog functions
\r
6136 \*---------------------------------------------------------------------------*/
\r
6139 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6141 char move[MSG_SIZ];
\r
6144 switch (message) {
\r
6145 case WM_INITDIALOG:
\r
6146 move[0] = (char) lParam;
\r
6147 move[1] = NULLCHAR;
\r
6148 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6149 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6150 SetWindowText(hInput, move);
\r
6152 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6156 switch (LOWORD(wParam)) {
\r
6158 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6159 appData.userName = strdup(move);
\r
6162 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6163 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6164 DisplayTitle(move);
\r
6168 EndDialog(hDlg, TRUE);
\r
6171 EndDialog(hDlg, FALSE);
\r
6182 PopUpNameDialog(char firstchar)
\r
6186 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6187 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6188 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6189 FreeProcInstance(lpProc);
\r
6192 /*---------------------------------------------------------------------------*\
\r
6196 \*---------------------------------------------------------------------------*/
\r
6198 /* Nonmodal error box */
\r
6199 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6200 WPARAM wParam, LPARAM lParam);
\r
6203 ErrorPopUp(char *title, char *content)
\r
6207 BOOLEAN modal = hwndMain == NULL;
\r
6225 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6226 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6229 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6231 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6232 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6233 hwndMain, (DLGPROC)lpProc);
\r
6234 FreeProcInstance(lpProc);
\r
6241 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6242 if (errorDialog == NULL) return;
\r
6243 DestroyWindow(errorDialog);
\r
6244 errorDialog = NULL;
\r
6245 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6249 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6254 switch (message) {
\r
6255 case WM_INITDIALOG:
\r
6256 GetWindowRect(hDlg, &rChild);
\r
6259 SetWindowPos(hDlg, NULL, rChild.left,
\r
6260 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6261 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6265 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6266 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6267 and it doesn't work when you resize the dialog.
\r
6268 For now, just give it a default position.
\r
6270 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6272 errorDialog = hDlg;
\r
6273 SetWindowText(hDlg, errorTitle);
\r
6274 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6275 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6279 switch (LOWORD(wParam)) {
\r
6282 if (errorDialog == hDlg) errorDialog = NULL;
\r
6283 DestroyWindow(hDlg);
\r
6295 HWND gothicDialog = NULL;
\r
6298 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6302 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6304 switch (message) {
\r
6305 case WM_INITDIALOG:
\r
6306 GetWindowRect(hDlg, &rChild);
\r
6308 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6312 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6313 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6314 and it doesn't work when you resize the dialog.
\r
6315 For now, just give it a default position.
\r
6317 gothicDialog = hDlg;
\r
6318 SetWindowText(hDlg, errorTitle);
\r
6319 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6320 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6324 switch (LOWORD(wParam)) {
\r
6327 if (errorDialog == hDlg) errorDialog = NULL;
\r
6328 DestroyWindow(hDlg);
\r
6340 GothicPopUp(char *title, VariantClass variant)
\r
6343 static char *lastTitle;
\r
6345 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6346 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6348 if(lastTitle != title && gothicDialog != NULL) {
\r
6349 DestroyWindow(gothicDialog);
\r
6350 gothicDialog = NULL;
\r
6352 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6353 title = lastTitle;
\r
6354 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6355 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6356 hwndMain, (DLGPROC)lpProc);
\r
6357 FreeProcInstance(lpProc);
\r
6362 /*---------------------------------------------------------------------------*\
\r
6364 * Ics Interaction console functions
\r
6366 \*---------------------------------------------------------------------------*/
\r
6368 #define HISTORY_SIZE 64
\r
6369 static char *history[HISTORY_SIZE];
\r
6370 int histIn = 0, histP = 0;
\r
6373 SaveInHistory(char *cmd)
\r
6375 if (history[histIn] != NULL) {
\r
6376 free(history[histIn]);
\r
6377 history[histIn] = NULL;
\r
6379 if (*cmd == NULLCHAR) return;
\r
6380 history[histIn] = StrSave(cmd);
\r
6381 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6382 if (history[histIn] != NULL) {
\r
6383 free(history[histIn]);
\r
6384 history[histIn] = NULL;
\r
6390 PrevInHistory(char *cmd)
\r
6393 if (histP == histIn) {
\r
6394 if (history[histIn] != NULL) free(history[histIn]);
\r
6395 history[histIn] = StrSave(cmd);
\r
6397 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6398 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6400 return history[histP];
\r
6406 if (histP == histIn) return NULL;
\r
6407 histP = (histP + 1) % HISTORY_SIZE;
\r
6408 return history[histP];
\r
6412 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6416 hmenu = LoadMenu(hInst, "TextMenu");
\r
6417 h = GetSubMenu(hmenu, 0);
\r
6419 if (strcmp(e->item, "-") == 0) {
\r
6420 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6421 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6422 int flags = MF_STRING, j = 0;
\r
6423 if (e->item[0] == '|') {
\r
6424 flags |= MF_MENUBARBREAK;
\r
6427 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6428 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6436 WNDPROC consoleTextWindowProc;
\r
6439 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6441 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6442 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6446 SetWindowText(hInput, command);
\r
6448 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6450 sel.cpMin = 999999;
\r
6451 sel.cpMax = 999999;
\r
6452 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6457 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6458 if (sel.cpMin == sel.cpMax) {
\r
6459 /* Expand to surrounding word */
\r
6462 tr.chrg.cpMax = sel.cpMin;
\r
6463 tr.chrg.cpMin = --sel.cpMin;
\r
6464 if (sel.cpMin < 0) break;
\r
6465 tr.lpstrText = name;
\r
6466 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6467 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6471 tr.chrg.cpMin = sel.cpMax;
\r
6472 tr.chrg.cpMax = ++sel.cpMax;
\r
6473 tr.lpstrText = name;
\r
6474 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6475 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6478 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6479 MessageBeep(MB_ICONEXCLAMATION);
\r
6483 tr.lpstrText = name;
\r
6484 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6486 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6487 MessageBeep(MB_ICONEXCLAMATION);
\r
6490 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6493 sprintf(buf, "%s %s", command, name);
\r
6494 SetWindowText(hInput, buf);
\r
6495 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6497 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6498 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6499 SetWindowText(hInput, buf);
\r
6500 sel.cpMin = 999999;
\r
6501 sel.cpMax = 999999;
\r
6502 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6508 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6513 switch (message) {
\r
6515 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6518 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6521 sel.cpMin = 999999;
\r
6522 sel.cpMax = 999999;
\r
6523 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6524 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6529 if(wParam != '\022') {
\r
6530 if (wParam == '\t') {
\r
6531 if (GetKeyState(VK_SHIFT) < 0) {
\r
6533 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6534 if (buttonDesc[0].hwnd) {
\r
6535 SetFocus(buttonDesc[0].hwnd);
\r
6537 SetFocus(hwndMain);
\r
6541 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6544 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6545 JAWS_DELETE( SetFocus(hInput); )
\r
6546 SendMessage(hInput, message, wParam, lParam);
\r
6549 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6550 case WM_RBUTTONDOWN:
\r
6551 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6552 /* Move selection here if it was empty */
\r
6554 pt.x = LOWORD(lParam);
\r
6555 pt.y = HIWORD(lParam);
\r
6556 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6557 if (sel.cpMin == sel.cpMax) {
\r
6558 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6559 sel.cpMax = sel.cpMin;
\r
6560 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6562 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6563 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6565 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6566 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6567 if (sel.cpMin == sel.cpMax) {
\r
6568 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6569 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6571 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6572 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6574 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6575 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6576 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6577 MenuPopup(hwnd, pt, hmenu, -1);
\r
6581 case WM_RBUTTONUP:
\r
6582 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6583 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6584 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6588 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6590 return SendMessage(hInput, message, wParam, lParam);
\r
6591 case WM_MBUTTONDOWN:
\r
6592 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6594 switch (LOWORD(wParam)) {
\r
6595 case IDM_QuickPaste:
\r
6597 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6598 if (sel.cpMin == sel.cpMax) {
\r
6599 MessageBeep(MB_ICONEXCLAMATION);
\r
6602 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6603 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6604 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6609 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6612 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6615 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6619 int i = LOWORD(wParam) - IDM_CommandX;
\r
6620 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6621 icsTextMenuEntry[i].command != NULL) {
\r
6622 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6623 icsTextMenuEntry[i].getname,
\r
6624 icsTextMenuEntry[i].immediate);
\r
6632 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6635 WNDPROC consoleInputWindowProc;
\r
6638 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6640 char buf[MSG_SIZ];
\r
6642 static BOOL sendNextChar = FALSE;
\r
6643 static BOOL quoteNextChar = FALSE;
\r
6644 InputSource *is = consoleInputSource;
\r
6648 switch (message) {
\r
6650 if (!appData.localLineEditing || sendNextChar) {
\r
6651 is->buf[0] = (CHAR) wParam;
\r
6653 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6654 sendNextChar = FALSE;
\r
6657 if (quoteNextChar) {
\r
6658 buf[0] = (char) wParam;
\r
6659 buf[1] = NULLCHAR;
\r
6660 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6661 quoteNextChar = FALSE;
\r
6665 case '\r': /* Enter key */
\r
6666 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6667 if (consoleEcho) SaveInHistory(is->buf);
\r
6668 is->buf[is->count++] = '\n';
\r
6669 is->buf[is->count] = NULLCHAR;
\r
6670 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6671 if (consoleEcho) {
\r
6672 ConsoleOutput(is->buf, is->count, TRUE);
\r
6673 } else if (appData.localLineEditing) {
\r
6674 ConsoleOutput("\n", 1, TRUE);
\r
6677 case '\033': /* Escape key */
\r
6678 SetWindowText(hwnd, "");
\r
6679 cf.cbSize = sizeof(CHARFORMAT);
\r
6680 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6681 if (consoleEcho) {
\r
6682 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6684 cf.crTextColor = COLOR_ECHOOFF;
\r
6686 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6687 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6689 case '\t': /* Tab key */
\r
6690 if (GetKeyState(VK_SHIFT) < 0) {
\r
6692 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6695 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6696 if (buttonDesc[0].hwnd) {
\r
6697 SetFocus(buttonDesc[0].hwnd);
\r
6699 SetFocus(hwndMain);
\r
6703 case '\023': /* Ctrl+S */
\r
6704 sendNextChar = TRUE;
\r
6706 case '\021': /* Ctrl+Q */
\r
6707 quoteNextChar = TRUE;
\r
6717 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6718 p = PrevInHistory(buf);
\r
6720 SetWindowText(hwnd, p);
\r
6721 sel.cpMin = 999999;
\r
6722 sel.cpMax = 999999;
\r
6723 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6728 p = NextInHistory();
\r
6730 SetWindowText(hwnd, p);
\r
6731 sel.cpMin = 999999;
\r
6732 sel.cpMax = 999999;
\r
6733 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6739 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6743 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6747 case WM_MBUTTONDOWN:
\r
6748 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6749 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6751 case WM_RBUTTONUP:
\r
6752 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6753 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6754 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6758 hmenu = LoadMenu(hInst, "InputMenu");
\r
6759 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6760 if (sel.cpMin == sel.cpMax) {
\r
6761 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6762 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6764 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6765 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6767 pt.x = LOWORD(lParam);
\r
6768 pt.y = HIWORD(lParam);
\r
6769 MenuPopup(hwnd, pt, hmenu, -1);
\r
6773 switch (LOWORD(wParam)) {
\r
6775 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6777 case IDM_SelectAll:
\r
6779 sel.cpMax = -1; /*999999?*/
\r
6780 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6783 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6786 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6789 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6794 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6797 #define CO_MAX 100000
\r
6798 #define CO_TRIM 1000
\r
6801 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6803 static SnapData sd;
\r
6804 HWND hText, hInput;
\r
6806 static int sizeX, sizeY;
\r
6807 int newSizeX, newSizeY;
\r
6811 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6812 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6814 switch (message) {
\r
6816 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6818 ENLINK *pLink = (ENLINK*)lParam;
\r
6819 if (pLink->msg == WM_LBUTTONUP)
\r
6823 tr.chrg = pLink->chrg;
\r
6824 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6825 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6826 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6827 free(tr.lpstrText);
\r
6831 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6832 hwndConsole = hDlg;
\r
6834 consoleTextWindowProc = (WNDPROC)
\r
6835 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6836 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6837 consoleInputWindowProc = (WNDPROC)
\r
6838 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6839 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6840 Colorize(ColorNormal, TRUE);
\r
6841 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6842 ChangedConsoleFont();
\r
6843 GetClientRect(hDlg, &rect);
\r
6844 sizeX = rect.right;
\r
6845 sizeY = rect.bottom;
\r
6846 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6847 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6848 WINDOWPLACEMENT wp;
\r
6849 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6850 wp.length = sizeof(WINDOWPLACEMENT);
\r
6852 wp.showCmd = SW_SHOW;
\r
6853 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6854 wp.rcNormalPosition.left = wpConsole.x;
\r
6855 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6856 wp.rcNormalPosition.top = wpConsole.y;
\r
6857 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6858 SetWindowPlacement(hDlg, &wp);
\r
6861 // [HGM] Chessknight's change 2004-07-13
\r
6862 else { /* Determine Defaults */
\r
6863 WINDOWPLACEMENT wp;
\r
6864 wpConsole.x = wpMain.width + 1;
\r
6865 wpConsole.y = wpMain.y;
\r
6866 wpConsole.width = screenWidth - wpMain.width;
\r
6867 wpConsole.height = wpMain.height;
\r
6868 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6869 wp.length = sizeof(WINDOWPLACEMENT);
\r
6871 wp.showCmd = SW_SHOW;
\r
6872 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6873 wp.rcNormalPosition.left = wpConsole.x;
\r
6874 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6875 wp.rcNormalPosition.top = wpConsole.y;
\r
6876 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6877 SetWindowPlacement(hDlg, &wp);
\r
6880 // Allow hText to highlight URLs and send notifications on them
\r
6881 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6882 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6883 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6884 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6898 if (IsIconic(hDlg)) break;
\r
6899 newSizeX = LOWORD(lParam);
\r
6900 newSizeY = HIWORD(lParam);
\r
6901 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6902 RECT rectText, rectInput;
\r
6904 int newTextHeight, newTextWidth;
\r
6905 GetWindowRect(hText, &rectText);
\r
6906 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6907 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6908 if (newTextHeight < 0) {
\r
6909 newSizeY += -newTextHeight;
\r
6910 newTextHeight = 0;
\r
6912 SetWindowPos(hText, NULL, 0, 0,
\r
6913 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6914 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6915 pt.x = rectInput.left;
\r
6916 pt.y = rectInput.top + newSizeY - sizeY;
\r
6917 ScreenToClient(hDlg, &pt);
\r
6918 SetWindowPos(hInput, NULL,
\r
6919 pt.x, pt.y, /* needs client coords */
\r
6920 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6921 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6927 case WM_GETMINMAXINFO:
\r
6928 /* Prevent resizing window too small */
\r
6929 mmi = (MINMAXINFO *) lParam;
\r
6930 mmi->ptMinTrackSize.x = 100;
\r
6931 mmi->ptMinTrackSize.y = 100;
\r
6934 /* [AS] Snapping */
\r
6935 case WM_ENTERSIZEMOVE:
\r
6936 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6939 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6942 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6944 case WM_EXITSIZEMOVE:
\r
6945 UpdateICSWidth(hText);
\r
6946 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6949 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6957 if (hwndConsole) return;
\r
6958 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6959 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6964 ConsoleOutput(char* data, int length, int forceVisible)
\r
6969 char buf[CO_MAX+1];
\r
6972 static int delayLF = 0;
\r
6973 CHARRANGE savesel, sel;
\r
6975 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6983 while (length--) {
\r
6991 } else if (*p == '\007') {
\r
6992 MyPlaySound(&sounds[(int)SoundBell]);
\r
6999 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7000 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7001 /* Save current selection */
\r
7002 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7003 exlen = GetWindowTextLength(hText);
\r
7004 /* Find out whether current end of text is visible */
\r
7005 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7006 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7007 /* Trim existing text if it's too long */
\r
7008 if (exlen + (q - buf) > CO_MAX) {
\r
7009 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7012 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7013 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7015 savesel.cpMin -= trim;
\r
7016 savesel.cpMax -= trim;
\r
7017 if (exlen < 0) exlen = 0;
\r
7018 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7019 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7021 /* Append the new text */
\r
7022 sel.cpMin = exlen;
\r
7023 sel.cpMax = exlen;
\r
7024 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7025 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7026 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7027 if (forceVisible || exlen == 0 ||
\r
7028 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7029 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7030 /* Scroll to make new end of text visible if old end of text
\r
7031 was visible or new text is an echo of user typein */
\r
7032 sel.cpMin = 9999999;
\r
7033 sel.cpMax = 9999999;
\r
7034 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7035 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7036 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7037 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7039 if (savesel.cpMax == exlen || forceVisible) {
\r
7040 /* Move insert point to new end of text if it was at the old
\r
7041 end of text or if the new text is an echo of user typein */
\r
7042 sel.cpMin = 9999999;
\r
7043 sel.cpMax = 9999999;
\r
7044 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7046 /* Restore previous selection */
\r
7047 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7049 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7056 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7060 COLORREF oldFg, oldBg;
\r
7064 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7066 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7067 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7068 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7071 rect.right = x + squareSize;
\r
7073 rect.bottom = y + squareSize;
\r
7076 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7077 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7078 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7079 &rect, str, strlen(str), NULL);
\r
7081 (void) SetTextColor(hdc, oldFg);
\r
7082 (void) SetBkColor(hdc, oldBg);
\r
7083 (void) SelectObject(hdc, oldFont);
\r
7087 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7088 RECT *rect, char *color, char *flagFell)
\r
7092 COLORREF oldFg, oldBg;
\r
7095 if (appData.clockMode) {
\r
7097 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7099 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7106 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7107 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7109 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7110 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7112 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7116 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7117 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7118 rect, str, strlen(str), NULL);
\r
7119 if(logoHeight > 0 && appData.clockMode) {
\r
7121 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7122 r.top = rect->top + logoHeight/2;
\r
7123 r.left = rect->left;
\r
7124 r.right = rect->right;
\r
7125 r.bottom = rect->bottom;
\r
7126 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7127 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7128 &r, str, strlen(str), NULL);
\r
7130 (void) SetTextColor(hdc, oldFg);
\r
7131 (void) SetBkColor(hdc, oldBg);
\r
7132 (void) SelectObject(hdc, oldFont);
\r
7137 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7143 if( count <= 0 ) {
\r
7144 if (appData.debugMode) {
\r
7145 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7148 return ERROR_INVALID_USER_BUFFER;
\r
7151 ResetEvent(ovl->hEvent);
\r
7152 ovl->Offset = ovl->OffsetHigh = 0;
\r
7153 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7157 err = GetLastError();
\r
7158 if (err == ERROR_IO_PENDING) {
\r
7159 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7163 err = GetLastError();
\r
7170 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7175 ResetEvent(ovl->hEvent);
\r
7176 ovl->Offset = ovl->OffsetHigh = 0;
\r
7177 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7181 err = GetLastError();
\r
7182 if (err == ERROR_IO_PENDING) {
\r
7183 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7187 err = GetLastError();
\r
7193 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7194 void CheckForInputBufferFull( InputSource * is )
\r
7196 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7197 /* Look for end of line */
\r
7198 char * p = is->buf;
\r
7200 while( p < is->next && *p != '\n' ) {
\r
7204 if( p >= is->next ) {
\r
7205 if (appData.debugMode) {
\r
7206 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7209 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7210 is->count = (DWORD) -1;
\r
7211 is->next = is->buf;
\r
7217 InputThread(LPVOID arg)
\r
7222 is = (InputSource *) arg;
\r
7223 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7224 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7225 while (is->hThread != NULL) {
\r
7226 is->error = DoReadFile(is->hFile, is->next,
\r
7227 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7228 &is->count, &ovl);
\r
7229 if (is->error == NO_ERROR) {
\r
7230 is->next += is->count;
\r
7232 if (is->error == ERROR_BROKEN_PIPE) {
\r
7233 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7236 is->count = (DWORD) -1;
\r
7237 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7242 CheckForInputBufferFull( is );
\r
7244 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7246 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7248 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7251 CloseHandle(ovl.hEvent);
\r
7252 CloseHandle(is->hFile);
\r
7254 if (appData.debugMode) {
\r
7255 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7262 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7264 NonOvlInputThread(LPVOID arg)
\r
7271 is = (InputSource *) arg;
\r
7272 while (is->hThread != NULL) {
\r
7273 is->error = ReadFile(is->hFile, is->next,
\r
7274 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7275 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7276 if (is->error == NO_ERROR) {
\r
7277 /* Change CRLF to LF */
\r
7278 if (is->next > is->buf) {
\r
7280 i = is->count + 1;
\r
7288 if (prev == '\r' && *p == '\n') {
\r
7300 if (is->error == ERROR_BROKEN_PIPE) {
\r
7301 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7304 is->count = (DWORD) -1;
\r
7308 CheckForInputBufferFull( is );
\r
7310 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7312 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7314 if (is->count < 0) break; /* Quit on error */
\r
7316 CloseHandle(is->hFile);
\r
7321 SocketInputThread(LPVOID arg)
\r
7325 is = (InputSource *) arg;
\r
7326 while (is->hThread != NULL) {
\r
7327 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7328 if ((int)is->count == SOCKET_ERROR) {
\r
7329 is->count = (DWORD) -1;
\r
7330 is->error = WSAGetLastError();
\r
7332 is->error = NO_ERROR;
\r
7333 is->next += is->count;
\r
7334 if (is->count == 0 && is->second == is) {
\r
7335 /* End of file on stderr; quit with no message */
\r
7339 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7341 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7343 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7349 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7353 is = (InputSource *) lParam;
\r
7354 if (is->lineByLine) {
\r
7355 /* Feed in lines one by one */
\r
7356 char *p = is->buf;
\r
7358 while (q < is->next) {
\r
7359 if (*q++ == '\n') {
\r
7360 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7365 /* Move any partial line to the start of the buffer */
\r
7367 while (p < is->next) {
\r
7372 if (is->error != NO_ERROR || is->count == 0) {
\r
7373 /* Notify backend of the error. Note: If there was a partial
\r
7374 line at the end, it is not flushed through. */
\r
7375 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7378 /* Feed in the whole chunk of input at once */
\r
7379 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7380 is->next = is->buf;
\r
7384 /*---------------------------------------------------------------------------*\
\r
7386 * Menu enables. Used when setting various modes.
\r
7388 \*---------------------------------------------------------------------------*/
\r
7396 GreyRevert(Boolean grey)
\r
7397 { // [HGM] vari: for retracting variations in local mode
\r
7398 HMENU hmenu = GetMenu(hwndMain);
\r
7399 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7400 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7404 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7406 while (enab->item > 0) {
\r
7407 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7412 Enables gnuEnables[] = {
\r
7413 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7415 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7416 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7417 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7418 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7419 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7420 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7421 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7422 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7424 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7425 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7429 Enables icsEnables[] = {
\r
7430 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7437 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7439 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7440 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7441 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7442 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7443 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7444 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7445 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7450 Enables zippyEnables[] = {
\r
7451 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7452 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7453 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7454 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7459 Enables ncpEnables[] = {
\r
7460 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7461 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7463 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7464 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7465 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7466 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7467 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7469 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7471 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7472 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7473 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7474 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7475 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7476 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7477 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7479 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7483 Enables trainingOnEnables[] = {
\r
7484 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7485 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7486 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7487 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7488 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7489 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7490 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7491 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7495 Enables trainingOffEnables[] = {
\r
7496 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7497 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7498 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7499 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7500 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7501 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7502 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7503 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7507 /* These modify either ncpEnables or gnuEnables */
\r
7508 Enables cmailEnables[] = {
\r
7509 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7510 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7511 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7512 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7513 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7514 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7515 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7519 Enables machineThinkingEnables[] = {
\r
7520 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7521 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7522 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7523 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7524 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7525 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7526 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7527 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7528 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7529 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7530 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7531 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7532 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7533 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7534 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7538 Enables userThinkingEnables[] = {
\r
7539 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7540 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7541 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7542 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7543 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7544 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7545 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7546 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7547 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7548 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7549 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7550 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7551 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7552 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7553 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7557 /*---------------------------------------------------------------------------*\
\r
7559 * Front-end interface functions exported by XBoard.
\r
7560 * Functions appear in same order as prototypes in frontend.h.
\r
7562 \*---------------------------------------------------------------------------*/
\r
7566 static UINT prevChecked = 0;
\r
7567 static int prevPausing = 0;
\r
7570 if (pausing != prevPausing) {
\r
7571 prevPausing = pausing;
\r
7572 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7573 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7574 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7577 switch (gameMode) {
\r
7578 case BeginningOfGame:
\r
7579 if (appData.icsActive)
\r
7580 nowChecked = IDM_IcsClient;
\r
7581 else if (appData.noChessProgram)
\r
7582 nowChecked = IDM_EditGame;
\r
7584 nowChecked = IDM_MachineBlack;
\r
7586 case MachinePlaysBlack:
\r
7587 nowChecked = IDM_MachineBlack;
\r
7589 case MachinePlaysWhite:
\r
7590 nowChecked = IDM_MachineWhite;
\r
7592 case TwoMachinesPlay:
\r
7593 nowChecked = IDM_TwoMachines;
\r
7596 nowChecked = IDM_AnalysisMode;
\r
7599 nowChecked = IDM_AnalyzeFile;
\r
7602 nowChecked = IDM_EditGame;
\r
7604 case PlayFromGameFile:
\r
7605 nowChecked = IDM_LoadGame;
\r
7607 case EditPosition:
\r
7608 nowChecked = IDM_EditPosition;
\r
7611 nowChecked = IDM_Training;
\r
7613 case IcsPlayingWhite:
\r
7614 case IcsPlayingBlack:
\r
7615 case IcsObserving:
\r
7617 nowChecked = IDM_IcsClient;
\r
7624 if (prevChecked != 0)
\r
7625 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7626 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7627 if (nowChecked != 0)
\r
7628 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7629 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7631 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7632 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7633 MF_BYCOMMAND|MF_ENABLED);
\r
7635 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7636 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7639 prevChecked = nowChecked;
\r
7641 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7642 if (appData.icsActive) {
\r
7643 if (appData.icsEngineAnalyze) {
\r
7644 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7645 MF_BYCOMMAND|MF_CHECKED);
\r
7647 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7648 MF_BYCOMMAND|MF_UNCHECKED);
\r
7656 HMENU hmenu = GetMenu(hwndMain);
\r
7657 SetMenuEnables(hmenu, icsEnables);
\r
7658 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7659 MF_BYPOSITION|MF_ENABLED);
\r
7661 if (appData.zippyPlay) {
\r
7662 SetMenuEnables(hmenu, zippyEnables);
\r
7663 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7664 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7665 MF_BYCOMMAND|MF_ENABLED);
\r
7673 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7679 HMENU hmenu = GetMenu(hwndMain);
\r
7680 SetMenuEnables(hmenu, ncpEnables);
\r
7681 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7682 MF_BYPOSITION|MF_GRAYED);
\r
7683 DrawMenuBar(hwndMain);
\r
7689 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7693 SetTrainingModeOn()
\r
7696 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7697 for (i = 0; i < N_BUTTONS; i++) {
\r
7698 if (buttonDesc[i].hwnd != NULL)
\r
7699 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7704 VOID SetTrainingModeOff()
\r
7707 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7708 for (i = 0; i < N_BUTTONS; i++) {
\r
7709 if (buttonDesc[i].hwnd != NULL)
\r
7710 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7716 SetUserThinkingEnables()
\r
7718 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7722 SetMachineThinkingEnables()
\r
7724 HMENU hMenu = GetMenu(hwndMain);
\r
7725 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7727 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7729 if (gameMode == MachinePlaysBlack) {
\r
7730 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7731 } else if (gameMode == MachinePlaysWhite) {
\r
7732 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7733 } else if (gameMode == TwoMachinesPlay) {
\r
7734 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7740 DisplayTitle(char *str)
\r
7742 char title[MSG_SIZ], *host;
\r
7743 if (str[0] != NULLCHAR) {
\r
7744 strcpy(title, str);
\r
7745 } else if (appData.icsActive) {
\r
7746 if (appData.icsCommPort[0] != NULLCHAR)
\r
7749 host = appData.icsHost;
\r
7750 sprintf(title, "%s: %s", szTitle, host);
\r
7751 } else if (appData.noChessProgram) {
\r
7752 strcpy(title, szTitle);
\r
7754 strcpy(title, szTitle);
\r
7755 strcat(title, ": ");
\r
7756 strcat(title, first.tidy);
\r
7758 SetWindowText(hwndMain, title);
\r
7763 DisplayMessage(char *str1, char *str2)
\r
7767 int remain = MESSAGE_TEXT_MAX - 1;
\r
7770 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7771 messageText[0] = NULLCHAR;
\r
7773 len = strlen(str1);
\r
7774 if (len > remain) len = remain;
\r
7775 strncpy(messageText, str1, len);
\r
7776 messageText[len] = NULLCHAR;
\r
7779 if (*str2 && remain >= 2) {
\r
7781 strcat(messageText, " ");
\r
7784 len = strlen(str2);
\r
7785 if (len > remain) len = remain;
\r
7786 strncat(messageText, str2, len);
\r
7788 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7790 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7794 hdc = GetDC(hwndMain);
\r
7795 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7796 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7797 &messageRect, messageText, strlen(messageText), NULL);
\r
7798 (void) SelectObject(hdc, oldFont);
\r
7799 (void) ReleaseDC(hwndMain, hdc);
\r
7803 DisplayError(char *str, int error)
\r
7805 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7811 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7812 NULL, error, LANG_NEUTRAL,
\r
7813 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7815 sprintf(buf, "%s:\n%s", str, buf2);
\r
7817 ErrorMap *em = errmap;
\r
7818 while (em->err != 0 && em->err != error) em++;
\r
7819 if (em->err != 0) {
\r
7820 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7822 sprintf(buf, "%s:\nError code %d", str, error);
\r
7827 ErrorPopUp("Error", buf);
\r
7832 DisplayMoveError(char *str)
\r
7834 fromX = fromY = -1;
\r
7835 ClearHighlights();
\r
7836 DrawPosition(FALSE, NULL);
\r
7837 if (appData.popupMoveErrors) {
\r
7838 ErrorPopUp("Error", str);
\r
7840 DisplayMessage(str, "");
\r
7841 moveErrorMessageUp = TRUE;
\r
7846 DisplayFatalError(char *str, int error, int exitStatus)
\r
7848 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7850 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7853 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7854 NULL, error, LANG_NEUTRAL,
\r
7855 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7857 sprintf(buf, "%s:\n%s", str, buf2);
\r
7859 ErrorMap *em = errmap;
\r
7860 while (em->err != 0 && em->err != error) em++;
\r
7861 if (em->err != 0) {
\r
7862 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7864 sprintf(buf, "%s:\nError code %d", str, error);
\r
7869 if (appData.debugMode) {
\r
7870 fprintf(debugFP, "%s: %s\n", label, str);
\r
7872 if (appData.popupExitMessage) {
\r
7873 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7874 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7876 ExitEvent(exitStatus);
\r
7881 DisplayInformation(char *str)
\r
7883 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7888 DisplayNote(char *str)
\r
7890 ErrorPopUp("Note", str);
\r
7895 char *title, *question, *replyPrefix;
\r
7900 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7902 static QuestionParams *qp;
\r
7903 char reply[MSG_SIZ];
\r
7906 switch (message) {
\r
7907 case WM_INITDIALOG:
\r
7908 qp = (QuestionParams *) lParam;
\r
7909 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7910 SetWindowText(hDlg, qp->title);
\r
7911 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7912 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7916 switch (LOWORD(wParam)) {
\r
7918 strcpy(reply, qp->replyPrefix);
\r
7919 if (*reply) strcat(reply, " ");
\r
7920 len = strlen(reply);
\r
7921 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7922 strcat(reply, "\n");
\r
7923 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7924 EndDialog(hDlg, TRUE);
\r
7925 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7928 EndDialog(hDlg, FALSE);
\r
7939 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7941 QuestionParams qp;
\r
7945 qp.question = question;
\r
7946 qp.replyPrefix = replyPrefix;
\r
7948 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7949 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7950 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7951 FreeProcInstance(lpProc);
\r
7954 /* [AS] Pick FRC position */
\r
7955 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7957 static int * lpIndexFRC;
\r
7963 case WM_INITDIALOG:
\r
7964 lpIndexFRC = (int *) lParam;
\r
7966 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7968 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7969 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7970 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7971 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7976 switch( LOWORD(wParam) ) {
\r
7978 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7979 EndDialog( hDlg, 0 );
\r
7980 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7983 EndDialog( hDlg, 1 );
\r
7985 case IDC_NFG_Edit:
\r
7986 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7987 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7989 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7992 case IDC_NFG_Random:
\r
7993 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7994 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8007 int index = appData.defaultFrcPosition;
\r
8008 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8010 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8012 if( result == 0 ) {
\r
8013 appData.defaultFrcPosition = index;
\r
8019 /* [AS] Game list options. Refactored by HGM */
\r
8021 HWND gameListOptionsDialog;
\r
8023 // low-level front-end: clear text edit / list widget
\r
8027 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8030 // low-level front-end: clear text edit / list widget
\r
8032 GLT_DeSelectList()
\r
8034 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8037 // low-level front-end: append line to text edit / list widget
\r
8039 GLT_AddToList( char *name )
\r
8042 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8046 // low-level front-end: get line from text edit / list widget
\r
8048 GLT_GetFromList( int index, char *name )
\r
8051 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8057 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8059 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8060 int idx2 = idx1 + delta;
\r
8061 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8063 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8066 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8067 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8068 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8069 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8073 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8077 case WM_INITDIALOG:
\r
8078 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8080 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8082 /* Initialize list */
\r
8083 GLT_TagsToList( lpUserGLT );
\r
8085 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8090 switch( LOWORD(wParam) ) {
\r
8093 EndDialog( hDlg, 0 );
\r
8096 EndDialog( hDlg, 1 );
\r
8099 case IDC_GLT_Default:
\r
8100 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8103 case IDC_GLT_Restore:
\r
8104 GLT_TagsToList( appData.gameListTags );
\r
8108 GLT_MoveSelection( hDlg, -1 );
\r
8111 case IDC_GLT_Down:
\r
8112 GLT_MoveSelection( hDlg, +1 );
\r
8122 int GameListOptions()
\r
8125 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8127 strcpy( lpUserGLT, appData.gameListTags );
\r
8129 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8131 if( result == 0 ) {
\r
8132 /* [AS] Memory leak here! */
\r
8133 appData.gameListTags = strdup( lpUserGLT );
\r
8140 DisplayIcsInteractionTitle(char *str)
\r
8142 char consoleTitle[MSG_SIZ];
\r
8144 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8145 SetWindowText(hwndConsole, consoleTitle);
\r
8149 DrawPosition(int fullRedraw, Board board)
\r
8151 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8154 void NotifyFrontendLogin()
\r
8157 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8163 fromX = fromY = -1;
\r
8164 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8165 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8166 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8167 dragInfo.lastpos = dragInfo.pos;
\r
8168 dragInfo.start.x = dragInfo.start.y = -1;
\r
8169 dragInfo.from = dragInfo.start;
\r
8171 DrawPosition(TRUE, NULL);
\r
8177 CommentPopUp(char *title, char *str)
\r
8179 HWND hwnd = GetActiveWindow();
\r
8180 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8182 SetActiveWindow(hwnd);
\r
8186 CommentPopDown(void)
\r
8188 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8189 if (commentDialog) {
\r
8190 ShowWindow(commentDialog, SW_HIDE);
\r
8192 commentUp = FALSE;
\r
8196 EditCommentPopUp(int index, char *title, char *str)
\r
8198 EitherCommentPopUp(index, title, str, TRUE);
\r
8205 MyPlaySound(&sounds[(int)SoundMove]);
\r
8208 VOID PlayIcsWinSound()
\r
8210 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8213 VOID PlayIcsLossSound()
\r
8215 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8218 VOID PlayIcsDrawSound()
\r
8220 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8223 VOID PlayIcsUnfinishedSound()
\r
8225 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8231 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8239 consoleEcho = TRUE;
\r
8240 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8241 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8242 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8251 consoleEcho = FALSE;
\r
8252 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8253 /* This works OK: set text and background both to the same color */
\r
8255 cf.crTextColor = COLOR_ECHOOFF;
\r
8256 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8257 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8260 /* No Raw()...? */
\r
8262 void Colorize(ColorClass cc, int continuation)
\r
8264 currentColorClass = cc;
\r
8265 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8266 consoleCF.crTextColor = textAttribs[cc].color;
\r
8267 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8268 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8274 static char buf[MSG_SIZ];
\r
8275 DWORD bufsiz = MSG_SIZ;
\r
8277 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8278 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8280 if (!GetUserName(buf, &bufsiz)) {
\r
8281 /*DisplayError("Error getting user name", GetLastError());*/
\r
8282 strcpy(buf, "User");
\r
8290 static char buf[MSG_SIZ];
\r
8291 DWORD bufsiz = MSG_SIZ;
\r
8293 if (!GetComputerName(buf, &bufsiz)) {
\r
8294 /*DisplayError("Error getting host name", GetLastError());*/
\r
8295 strcpy(buf, "Unknown");
\r
8302 ClockTimerRunning()
\r
8304 return clockTimerEvent != 0;
\r
8310 if (clockTimerEvent == 0) return FALSE;
\r
8311 KillTimer(hwndMain, clockTimerEvent);
\r
8312 clockTimerEvent = 0;
\r
8317 StartClockTimer(long millisec)
\r
8319 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8320 (UINT) millisec, NULL);
\r
8324 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8327 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8329 if(appData.noGUI) return;
\r
8330 hdc = GetDC(hwndMain);
\r
8331 if (!IsIconic(hwndMain)) {
\r
8332 DisplayAClock(hdc, timeRemaining, highlight,
\r
8333 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8335 if (highlight && iconCurrent == iconBlack) {
\r
8336 iconCurrent = iconWhite;
\r
8337 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8338 if (IsIconic(hwndMain)) {
\r
8339 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8342 (void) ReleaseDC(hwndMain, hdc);
\r
8344 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8348 DisplayBlackClock(long timeRemaining, int highlight)
\r
8351 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8353 if(appData.noGUI) return;
\r
8354 hdc = GetDC(hwndMain);
\r
8355 if (!IsIconic(hwndMain)) {
\r
8356 DisplayAClock(hdc, timeRemaining, highlight,
\r
8357 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8359 if (highlight && iconCurrent == iconWhite) {
\r
8360 iconCurrent = iconBlack;
\r
8361 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8362 if (IsIconic(hwndMain)) {
\r
8363 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8366 (void) ReleaseDC(hwndMain, hdc);
\r
8368 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8373 LoadGameTimerRunning()
\r
8375 return loadGameTimerEvent != 0;
\r
8379 StopLoadGameTimer()
\r
8381 if (loadGameTimerEvent == 0) return FALSE;
\r
8382 KillTimer(hwndMain, loadGameTimerEvent);
\r
8383 loadGameTimerEvent = 0;
\r
8388 StartLoadGameTimer(long millisec)
\r
8390 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8391 (UINT) millisec, NULL);
\r
8399 char fileTitle[MSG_SIZ];
\r
8401 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8402 f = OpenFileDialog(hwndMain, "a", defName,
\r
8403 appData.oldSaveStyle ? "gam" : "pgn",
\r
8405 "Save Game to File", NULL, fileTitle, NULL);
\r
8407 SaveGame(f, 0, "");
\r
8414 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8416 if (delayedTimerEvent != 0) {
\r
8417 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8418 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8420 KillTimer(hwndMain, delayedTimerEvent);
\r
8421 delayedTimerEvent = 0;
\r
8422 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8423 delayedTimerCallback();
\r
8425 delayedTimerCallback = cb;
\r
8426 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8427 (UINT) millisec, NULL);
\r
8430 DelayedEventCallback
\r
8433 if (delayedTimerEvent) {
\r
8434 return delayedTimerCallback;
\r
8441 CancelDelayedEvent()
\r
8443 if (delayedTimerEvent) {
\r
8444 KillTimer(hwndMain, delayedTimerEvent);
\r
8445 delayedTimerEvent = 0;
\r
8449 DWORD GetWin32Priority(int nice)
\r
8450 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8452 REALTIME_PRIORITY_CLASS 0x00000100
\r
8453 HIGH_PRIORITY_CLASS 0x00000080
\r
8454 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8455 NORMAL_PRIORITY_CLASS 0x00000020
\r
8456 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8457 IDLE_PRIORITY_CLASS 0x00000040
\r
8459 if (nice < -15) return 0x00000080;
\r
8460 if (nice < 0) return 0x00008000;
\r
8461 if (nice == 0) return 0x00000020;
\r
8462 if (nice < 15) return 0x00004000;
\r
8463 return 0x00000040;
\r
8466 /* Start a child process running the given program.
\r
8467 The process's standard output can be read from "from", and its
\r
8468 standard input can be written to "to".
\r
8469 Exit with fatal error if anything goes wrong.
\r
8470 Returns an opaque pointer that can be used to destroy the process
\r
8474 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8476 #define BUFSIZE 4096
\r
8478 HANDLE hChildStdinRd, hChildStdinWr,
\r
8479 hChildStdoutRd, hChildStdoutWr;
\r
8480 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8481 SECURITY_ATTRIBUTES saAttr;
\r
8483 PROCESS_INFORMATION piProcInfo;
\r
8484 STARTUPINFO siStartInfo;
\r
8486 char buf[MSG_SIZ];
\r
8489 if (appData.debugMode) {
\r
8490 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8495 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8496 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8497 saAttr.bInheritHandle = TRUE;
\r
8498 saAttr.lpSecurityDescriptor = NULL;
\r
8501 * The steps for redirecting child's STDOUT:
\r
8502 * 1. Create anonymous pipe to be STDOUT for child.
\r
8503 * 2. Create a noninheritable duplicate of read handle,
\r
8504 * and close the inheritable read handle.
\r
8507 /* Create a pipe for the child's STDOUT. */
\r
8508 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8509 return GetLastError();
\r
8512 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8513 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8514 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8515 FALSE, /* not inherited */
\r
8516 DUPLICATE_SAME_ACCESS);
\r
8518 return GetLastError();
\r
8520 CloseHandle(hChildStdoutRd);
\r
8523 * The steps for redirecting child's STDIN:
\r
8524 * 1. Create anonymous pipe to be STDIN for child.
\r
8525 * 2. Create a noninheritable duplicate of write handle,
\r
8526 * and close the inheritable write handle.
\r
8529 /* Create a pipe for the child's STDIN. */
\r
8530 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8531 return GetLastError();
\r
8534 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8535 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8536 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8537 FALSE, /* not inherited */
\r
8538 DUPLICATE_SAME_ACCESS);
\r
8540 return GetLastError();
\r
8542 CloseHandle(hChildStdinWr);
\r
8544 /* Arrange to (1) look in dir for the child .exe file, and
\r
8545 * (2) have dir be the child's working directory. Interpret
\r
8546 * dir relative to the directory WinBoard loaded from. */
\r
8547 GetCurrentDirectory(MSG_SIZ, buf);
\r
8548 SetCurrentDirectory(installDir);
\r
8549 SetCurrentDirectory(dir);
\r
8551 /* Now create the child process. */
\r
8553 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8554 siStartInfo.lpReserved = NULL;
\r
8555 siStartInfo.lpDesktop = NULL;
\r
8556 siStartInfo.lpTitle = NULL;
\r
8557 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8558 siStartInfo.cbReserved2 = 0;
\r
8559 siStartInfo.lpReserved2 = NULL;
\r
8560 siStartInfo.hStdInput = hChildStdinRd;
\r
8561 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8562 siStartInfo.hStdError = hChildStdoutWr;
\r
8564 fSuccess = CreateProcess(NULL,
\r
8565 cmdLine, /* command line */
\r
8566 NULL, /* process security attributes */
\r
8567 NULL, /* primary thread security attrs */
\r
8568 TRUE, /* handles are inherited */
\r
8569 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8570 NULL, /* use parent's environment */
\r
8572 &siStartInfo, /* STARTUPINFO pointer */
\r
8573 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8575 err = GetLastError();
\r
8576 SetCurrentDirectory(buf); /* return to prev directory */
\r
8581 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8582 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8583 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8586 /* Close the handles we don't need in the parent */
\r
8587 CloseHandle(piProcInfo.hThread);
\r
8588 CloseHandle(hChildStdinRd);
\r
8589 CloseHandle(hChildStdoutWr);
\r
8591 /* Prepare return value */
\r
8592 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8593 cp->kind = CPReal;
\r
8594 cp->hProcess = piProcInfo.hProcess;
\r
8595 cp->pid = piProcInfo.dwProcessId;
\r
8596 cp->hFrom = hChildStdoutRdDup;
\r
8597 cp->hTo = hChildStdinWrDup;
\r
8599 *pr = (void *) cp;
\r
8601 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8602 2000 where engines sometimes don't see the initial command(s)
\r
8603 from WinBoard and hang. I don't understand how that can happen,
\r
8604 but the Sleep is harmless, so I've put it in. Others have also
\r
8605 reported what may be the same problem, so hopefully this will fix
\r
8606 it for them too. */
\r
8614 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8616 ChildProc *cp; int result;
\r
8618 cp = (ChildProc *) pr;
\r
8619 if (cp == NULL) return;
\r
8621 switch (cp->kind) {
\r
8623 /* TerminateProcess is considered harmful, so... */
\r
8624 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8625 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8626 /* The following doesn't work because the chess program
\r
8627 doesn't "have the same console" as WinBoard. Maybe
\r
8628 we could arrange for this even though neither WinBoard
\r
8629 nor the chess program uses a console for stdio? */
\r
8630 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8632 /* [AS] Special termination modes for misbehaving programs... */
\r
8633 if( signal == 9 ) {
\r
8634 result = TerminateProcess( cp->hProcess, 0 );
\r
8636 if ( appData.debugMode) {
\r
8637 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8640 else if( signal == 10 ) {
\r
8641 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8643 if( dw != WAIT_OBJECT_0 ) {
\r
8644 result = TerminateProcess( cp->hProcess, 0 );
\r
8646 if ( appData.debugMode) {
\r
8647 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8653 CloseHandle(cp->hProcess);
\r
8657 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8661 closesocket(cp->sock);
\r
8666 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8667 closesocket(cp->sock);
\r
8668 closesocket(cp->sock2);
\r
8676 InterruptChildProcess(ProcRef pr)
\r
8680 cp = (ChildProc *) pr;
\r
8681 if (cp == NULL) return;
\r
8682 switch (cp->kind) {
\r
8684 /* The following doesn't work because the chess program
\r
8685 doesn't "have the same console" as WinBoard. Maybe
\r
8686 we could arrange for this even though neither WinBoard
\r
8687 nor the chess program uses a console for stdio */
\r
8688 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8693 /* Can't interrupt */
\r
8697 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8704 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8706 char cmdLine[MSG_SIZ];
\r
8708 if (port[0] == NULLCHAR) {
\r
8709 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8711 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8713 return StartChildProcess(cmdLine, "", pr);
\r
8717 /* Code to open TCP sockets */
\r
8720 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8725 struct sockaddr_in sa, mysa;
\r
8726 struct hostent FAR *hp;
\r
8727 unsigned short uport;
\r
8728 WORD wVersionRequested;
\r
8731 /* Initialize socket DLL */
\r
8732 wVersionRequested = MAKEWORD(1, 1);
\r
8733 err = WSAStartup(wVersionRequested, &wsaData);
\r
8734 if (err != 0) return err;
\r
8737 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8738 err = WSAGetLastError();
\r
8743 /* Bind local address using (mostly) don't-care values.
\r
8745 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8746 mysa.sin_family = AF_INET;
\r
8747 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8748 uport = (unsigned short) 0;
\r
8749 mysa.sin_port = htons(uport);
\r
8750 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8751 == SOCKET_ERROR) {
\r
8752 err = WSAGetLastError();
\r
8757 /* Resolve remote host name */
\r
8758 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8759 if (!(hp = gethostbyname(host))) {
\r
8760 unsigned int b0, b1, b2, b3;
\r
8762 err = WSAGetLastError();
\r
8764 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8765 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8766 hp->h_addrtype = AF_INET;
\r
8768 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8769 hp->h_addr_list[0] = (char *) malloc(4);
\r
8770 hp->h_addr_list[0][0] = (char) b0;
\r
8771 hp->h_addr_list[0][1] = (char) b1;
\r
8772 hp->h_addr_list[0][2] = (char) b2;
\r
8773 hp->h_addr_list[0][3] = (char) b3;
\r
8779 sa.sin_family = hp->h_addrtype;
\r
8780 uport = (unsigned short) atoi(port);
\r
8781 sa.sin_port = htons(uport);
\r
8782 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8784 /* Make connection */
\r
8785 if (connect(s, (struct sockaddr *) &sa,
\r
8786 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8787 err = WSAGetLastError();
\r
8792 /* Prepare return value */
\r
8793 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8794 cp->kind = CPSock;
\r
8796 *pr = (ProcRef *) cp;
\r
8802 OpenCommPort(char *name, ProcRef *pr)
\r
8807 char fullname[MSG_SIZ];
\r
8809 if (*name != '\\')
\r
8810 sprintf(fullname, "\\\\.\\%s", name);
\r
8812 strcpy(fullname, name);
\r
8814 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8815 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8816 if (h == (HANDLE) -1) {
\r
8817 return GetLastError();
\r
8821 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8823 /* Accumulate characters until a 100ms pause, then parse */
\r
8824 ct.ReadIntervalTimeout = 100;
\r
8825 ct.ReadTotalTimeoutMultiplier = 0;
\r
8826 ct.ReadTotalTimeoutConstant = 0;
\r
8827 ct.WriteTotalTimeoutMultiplier = 0;
\r
8828 ct.WriteTotalTimeoutConstant = 0;
\r
8829 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8831 /* Prepare return value */
\r
8832 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8833 cp->kind = CPComm;
\r
8836 *pr = (ProcRef *) cp;
\r
8842 OpenLoopback(ProcRef *pr)
\r
8844 DisplayFatalError("Not implemented", 0, 1);
\r
8850 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8855 struct sockaddr_in sa, mysa;
\r
8856 struct hostent FAR *hp;
\r
8857 unsigned short uport;
\r
8858 WORD wVersionRequested;
\r
8861 char stderrPortStr[MSG_SIZ];
\r
8863 /* Initialize socket DLL */
\r
8864 wVersionRequested = MAKEWORD(1, 1);
\r
8865 err = WSAStartup(wVersionRequested, &wsaData);
\r
8866 if (err != 0) return err;
\r
8868 /* Resolve remote host name */
\r
8869 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8870 if (!(hp = gethostbyname(host))) {
\r
8871 unsigned int b0, b1, b2, b3;
\r
8873 err = WSAGetLastError();
\r
8875 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8876 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8877 hp->h_addrtype = AF_INET;
\r
8879 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8880 hp->h_addr_list[0] = (char *) malloc(4);
\r
8881 hp->h_addr_list[0][0] = (char) b0;
\r
8882 hp->h_addr_list[0][1] = (char) b1;
\r
8883 hp->h_addr_list[0][2] = (char) b2;
\r
8884 hp->h_addr_list[0][3] = (char) b3;
\r
8890 sa.sin_family = hp->h_addrtype;
\r
8891 uport = (unsigned short) 514;
\r
8892 sa.sin_port = htons(uport);
\r
8893 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8895 /* Bind local socket to unused "privileged" port address
\r
8897 s = INVALID_SOCKET;
\r
8898 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8899 mysa.sin_family = AF_INET;
\r
8900 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8901 for (fromPort = 1023;; fromPort--) {
\r
8902 if (fromPort < 0) {
\r
8904 return WSAEADDRINUSE;
\r
8906 if (s == INVALID_SOCKET) {
\r
8907 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8908 err = WSAGetLastError();
\r
8913 uport = (unsigned short) fromPort;
\r
8914 mysa.sin_port = htons(uport);
\r
8915 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8916 == SOCKET_ERROR) {
\r
8917 err = WSAGetLastError();
\r
8918 if (err == WSAEADDRINUSE) continue;
\r
8922 if (connect(s, (struct sockaddr *) &sa,
\r
8923 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8924 err = WSAGetLastError();
\r
8925 if (err == WSAEADDRINUSE) {
\r
8936 /* Bind stderr local socket to unused "privileged" port address
\r
8938 s2 = INVALID_SOCKET;
\r
8939 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8940 mysa.sin_family = AF_INET;
\r
8941 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8942 for (fromPort = 1023;; fromPort--) {
\r
8943 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8944 if (fromPort < 0) {
\r
8945 (void) closesocket(s);
\r
8947 return WSAEADDRINUSE;
\r
8949 if (s2 == INVALID_SOCKET) {
\r
8950 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8951 err = WSAGetLastError();
\r
8957 uport = (unsigned short) fromPort;
\r
8958 mysa.sin_port = htons(uport);
\r
8959 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8960 == SOCKET_ERROR) {
\r
8961 err = WSAGetLastError();
\r
8962 if (err == WSAEADDRINUSE) continue;
\r
8963 (void) closesocket(s);
\r
8967 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8968 err = WSAGetLastError();
\r
8969 if (err == WSAEADDRINUSE) {
\r
8971 s2 = INVALID_SOCKET;
\r
8974 (void) closesocket(s);
\r
8975 (void) closesocket(s2);
\r
8981 prevStderrPort = fromPort; // remember port used
\r
8982 sprintf(stderrPortStr, "%d", fromPort);
\r
8984 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8985 err = WSAGetLastError();
\r
8986 (void) closesocket(s);
\r
8987 (void) closesocket(s2);
\r
8992 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8993 err = WSAGetLastError();
\r
8994 (void) closesocket(s);
\r
8995 (void) closesocket(s2);
\r
8999 if (*user == NULLCHAR) user = UserName();
\r
9000 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9001 err = WSAGetLastError();
\r
9002 (void) closesocket(s);
\r
9003 (void) closesocket(s2);
\r
9007 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9008 err = WSAGetLastError();
\r
9009 (void) closesocket(s);
\r
9010 (void) closesocket(s2);
\r
9015 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9016 err = WSAGetLastError();
\r
9017 (void) closesocket(s);
\r
9018 (void) closesocket(s2);
\r
9022 (void) closesocket(s2); /* Stop listening */
\r
9024 /* Prepare return value */
\r
9025 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9026 cp->kind = CPRcmd;
\r
9029 *pr = (ProcRef *) cp;
\r
9036 AddInputSource(ProcRef pr, int lineByLine,
\r
9037 InputCallback func, VOIDSTAR closure)
\r
9039 InputSource *is, *is2 = NULL;
\r
9040 ChildProc *cp = (ChildProc *) pr;
\r
9042 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9043 is->lineByLine = lineByLine;
\r
9045 is->closure = closure;
\r
9046 is->second = NULL;
\r
9047 is->next = is->buf;
\r
9048 if (pr == NoProc) {
\r
9049 is->kind = CPReal;
\r
9050 consoleInputSource = is;
\r
9052 is->kind = cp->kind;
\r
9054 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9055 we create all threads suspended so that the is->hThread variable can be
\r
9056 safely assigned, then let the threads start with ResumeThread.
\r
9058 switch (cp->kind) {
\r
9060 is->hFile = cp->hFrom;
\r
9061 cp->hFrom = NULL; /* now owned by InputThread */
\r
9063 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9064 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9068 is->hFile = cp->hFrom;
\r
9069 cp->hFrom = NULL; /* now owned by InputThread */
\r
9071 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9072 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9076 is->sock = cp->sock;
\r
9078 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9079 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9083 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9085 is->sock = cp->sock;
\r
9087 is2->sock = cp->sock2;
\r
9088 is2->second = is2;
\r
9090 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9091 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9093 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9094 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9098 if( is->hThread != NULL ) {
\r
9099 ResumeThread( is->hThread );
\r
9102 if( is2 != NULL && is2->hThread != NULL ) {
\r
9103 ResumeThread( is2->hThread );
\r
9107 return (InputSourceRef) is;
\r
9111 RemoveInputSource(InputSourceRef isr)
\r
9115 is = (InputSource *) isr;
\r
9116 is->hThread = NULL; /* tell thread to stop */
\r
9117 CloseHandle(is->hThread);
\r
9118 if (is->second != NULL) {
\r
9119 is->second->hThread = NULL;
\r
9120 CloseHandle(is->second->hThread);
\r
9124 int no_wrap(char *message, int count)
\r
9126 ConsoleOutput(message, count, FALSE);
\r
9131 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9134 int outCount = SOCKET_ERROR;
\r
9135 ChildProc *cp = (ChildProc *) pr;
\r
9136 static OVERLAPPED ovl;
\r
9137 static int line = 0;
\r
9141 if (appData.noJoin || !appData.useInternalWrap)
\r
9142 return no_wrap(message, count);
\r
9145 int width = get_term_width();
\r
9146 int len = wrap(NULL, message, count, width, &line);
\r
9147 char *msg = malloc(len);
\r
9151 return no_wrap(message, count);
\r
9154 dbgchk = wrap(msg, message, count, width, &line);
\r
9155 if (dbgchk != len && appData.debugMode)
\r
9156 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9157 ConsoleOutput(msg, len, FALSE);
\r
9164 if (ovl.hEvent == NULL) {
\r
9165 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9167 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9169 switch (cp->kind) {
\r
9172 outCount = send(cp->sock, message, count, 0);
\r
9173 if (outCount == SOCKET_ERROR) {
\r
9174 *outError = WSAGetLastError();
\r
9176 *outError = NO_ERROR;
\r
9181 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9182 &dOutCount, NULL)) {
\r
9183 *outError = NO_ERROR;
\r
9184 outCount = (int) dOutCount;
\r
9186 *outError = GetLastError();
\r
9191 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9192 &dOutCount, &ovl);
\r
9193 if (*outError == NO_ERROR) {
\r
9194 outCount = (int) dOutCount;
\r
9202 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9205 /* Ignore delay, not implemented for WinBoard */
\r
9206 return OutputToProcess(pr, message, count, outError);
\r
9211 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9212 char *buf, int count, int error)
\r
9214 DisplayFatalError("Not implemented", 0, 1);
\r
9217 /* see wgamelist.c for Game List functions */
\r
9218 /* see wedittags.c for Edit Tags functions */
\r
9225 char buf[MSG_SIZ];
\r
9228 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9229 f = fopen(buf, "r");
\r
9231 ProcessICSInitScript(f);
\r
9239 StartAnalysisClock()
\r
9241 if (analysisTimerEvent) return;
\r
9242 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9243 (UINT) 2000, NULL);
\r
9247 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9249 highlightInfo.sq[0].x = fromX;
\r
9250 highlightInfo.sq[0].y = fromY;
\r
9251 highlightInfo.sq[1].x = toX;
\r
9252 highlightInfo.sq[1].y = toY;
\r
9258 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9259 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9263 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9265 premoveHighlightInfo.sq[0].x = fromX;
\r
9266 premoveHighlightInfo.sq[0].y = fromY;
\r
9267 premoveHighlightInfo.sq[1].x = toX;
\r
9268 premoveHighlightInfo.sq[1].y = toY;
\r
9272 ClearPremoveHighlights()
\r
9274 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9275 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9279 ShutDownFrontEnd()
\r
9281 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9282 DeleteClipboardTempFiles();
\r
9288 if (IsIconic(hwndMain))
\r
9289 ShowWindow(hwndMain, SW_RESTORE);
\r
9291 SetActiveWindow(hwndMain);
\r
9295 * Prototypes for animation support routines
\r
9297 static void ScreenSquare(int column, int row, POINT * pt);
\r
9298 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9299 POINT frames[], int * nFrames);
\r
9303 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9304 { // [HGM] atomic: animate blast wave
\r
9306 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9307 explodeInfo.fromX = fromX;
\r
9308 explodeInfo.fromY = fromY;
\r
9309 explodeInfo.toX = toX;
\r
9310 explodeInfo.toY = toY;
\r
9311 for(i=1; i<nFrames; i++) {
\r
9312 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9313 DrawPosition(FALSE, NULL);
\r
9314 Sleep(appData.animSpeed);
\r
9316 explodeInfo.radius = 0;
\r
9317 DrawPosition(TRUE, NULL);
\r
9323 AnimateMove(board, fromX, fromY, toX, toY)
\r
9330 ChessSquare piece;
\r
9331 POINT start, finish, mid;
\r
9332 POINT frames[kFactor * 2 + 1];
\r
9335 if (!appData.animate) return;
\r
9336 if (doingSizing) return;
\r
9337 if (fromY < 0 || fromX < 0) return;
\r
9338 piece = board[fromY][fromX];
\r
9339 if (piece >= EmptySquare) return;
\r
9341 ScreenSquare(fromX, fromY, &start);
\r
9342 ScreenSquare(toX, toY, &finish);
\r
9344 /* All pieces except knights move in straight line */
\r
9345 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9346 mid.x = start.x + (finish.x - start.x) / 2;
\r
9347 mid.y = start.y + (finish.y - start.y) / 2;
\r
9349 /* Knight: make diagonal movement then straight */
\r
9350 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9351 mid.x = start.x + (finish.x - start.x) / 2;
\r
9355 mid.y = start.y + (finish.y - start.y) / 2;
\r
9359 /* Don't use as many frames for very short moves */
\r
9360 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9361 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9363 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9365 animInfo.from.x = fromX;
\r
9366 animInfo.from.y = fromY;
\r
9367 animInfo.to.x = toX;
\r
9368 animInfo.to.y = toY;
\r
9369 animInfo.lastpos = start;
\r
9370 animInfo.piece = piece;
\r
9371 for (n = 0; n < nFrames; n++) {
\r
9372 animInfo.pos = frames[n];
\r
9373 DrawPosition(FALSE, NULL);
\r
9374 animInfo.lastpos = animInfo.pos;
\r
9375 Sleep(appData.animSpeed);
\r
9377 animInfo.pos = finish;
\r
9378 DrawPosition(FALSE, NULL);
\r
9379 animInfo.piece = EmptySquare;
\r
9380 if(gameInfo.variant == VariantAtomic &&
\r
9381 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9382 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9385 /* Convert board position to corner of screen rect and color */
\r
9388 ScreenSquare(column, row, pt)
\r
9389 int column; int row; POINT * pt;
\r
9392 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9393 pt->y = lineGap + row * (squareSize + lineGap);
\r
9395 pt->x = lineGap + column * (squareSize + lineGap);
\r
9396 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9400 /* Generate a series of frame coords from start->mid->finish.
\r
9401 The movement rate doubles until the half way point is
\r
9402 reached, then halves back down to the final destination,
\r
9403 which gives a nice slow in/out effect. The algorithmn
\r
9404 may seem to generate too many intermediates for short
\r
9405 moves, but remember that the purpose is to attract the
\r
9406 viewers attention to the piece about to be moved and
\r
9407 then to where it ends up. Too few frames would be less
\r
9411 Tween(start, mid, finish, factor, frames, nFrames)
\r
9412 POINT * start; POINT * mid;
\r
9413 POINT * finish; int factor;
\r
9414 POINT frames[]; int * nFrames;
\r
9416 int n, fraction = 1, count = 0;
\r
9418 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9419 for (n = 0; n < factor; n++)
\r
9421 for (n = 0; n < factor; n++) {
\r
9422 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9423 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9425 fraction = fraction / 2;
\r
9429 frames[count] = *mid;
\r
9432 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9434 for (n = 0; n < factor; n++) {
\r
9435 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9436 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9438 fraction = fraction * 2;
\r
9444 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9446 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9448 EvalGraphSet( first, last, current, pvInfoList );
\r