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
1225 char *dummy, buf[MSG_SIZ];
\r
1226 if(name[0] == '%' && strchr(name+1, '%')) { // [HGM] recognize %*% as environment variable
\r
1227 strcpy(buf, name+1);
\r
1228 *strchr(buf, '%') = 0;
\r
1229 installDir = getenv(buf);
\r
1230 sprintf(fullname, "%s\\%s", installDir, strchr(name+1, '%')+1);
\r
1231 return strlen(fullname);
\r
1233 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1237 MyGetFullPathName(char *name, char *fullname)
\r
1240 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1245 { // [HGM] args: allows testing if main window is realized from back-end
\r
1246 return hwndMain != NULL;
\r
1250 PopUpStartupDialog()
\r
1254 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1255 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1256 FreeProcInstance(lpProc);
\r
1259 /*---------------------------------------------------------------------------*\
\r
1261 * GDI board drawing routines
\r
1263 \*---------------------------------------------------------------------------*/
\r
1265 /* [AS] Draw square using background texture */
\r
1266 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1271 return; /* Should never happen! */
\r
1274 SetGraphicsMode( dst, GM_ADVANCED );
\r
1281 /* X reflection */
\r
1286 x.eDx = (FLOAT) dw + dx - 1;
\r
1289 SetWorldTransform( dst, &x );
\r
1292 /* Y reflection */
\r
1298 x.eDy = (FLOAT) dh + dy - 1;
\r
1300 SetWorldTransform( dst, &x );
\r
1308 x.eDx = (FLOAT) dx;
\r
1309 x.eDy = (FLOAT) dy;
\r
1312 SetWorldTransform( dst, &x );
\r
1316 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1324 SetWorldTransform( dst, &x );
\r
1326 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1329 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1331 PM_WP = (int) WhitePawn,
\r
1332 PM_WN = (int) WhiteKnight,
\r
1333 PM_WB = (int) WhiteBishop,
\r
1334 PM_WR = (int) WhiteRook,
\r
1335 PM_WQ = (int) WhiteQueen,
\r
1336 PM_WF = (int) WhiteFerz,
\r
1337 PM_WW = (int) WhiteWazir,
\r
1338 PM_WE = (int) WhiteAlfil,
\r
1339 PM_WM = (int) WhiteMan,
\r
1340 PM_WO = (int) WhiteCannon,
\r
1341 PM_WU = (int) WhiteUnicorn,
\r
1342 PM_WH = (int) WhiteNightrider,
\r
1343 PM_WA = (int) WhiteAngel,
\r
1344 PM_WC = (int) WhiteMarshall,
\r
1345 PM_WAB = (int) WhiteCardinal,
\r
1346 PM_WD = (int) WhiteDragon,
\r
1347 PM_WL = (int) WhiteLance,
\r
1348 PM_WS = (int) WhiteCobra,
\r
1349 PM_WV = (int) WhiteFalcon,
\r
1350 PM_WSG = (int) WhiteSilver,
\r
1351 PM_WG = (int) WhiteGrasshopper,
\r
1352 PM_WK = (int) WhiteKing,
\r
1353 PM_BP = (int) BlackPawn,
\r
1354 PM_BN = (int) BlackKnight,
\r
1355 PM_BB = (int) BlackBishop,
\r
1356 PM_BR = (int) BlackRook,
\r
1357 PM_BQ = (int) BlackQueen,
\r
1358 PM_BF = (int) BlackFerz,
\r
1359 PM_BW = (int) BlackWazir,
\r
1360 PM_BE = (int) BlackAlfil,
\r
1361 PM_BM = (int) BlackMan,
\r
1362 PM_BO = (int) BlackCannon,
\r
1363 PM_BU = (int) BlackUnicorn,
\r
1364 PM_BH = (int) BlackNightrider,
\r
1365 PM_BA = (int) BlackAngel,
\r
1366 PM_BC = (int) BlackMarshall,
\r
1367 PM_BG = (int) BlackGrasshopper,
\r
1368 PM_BAB = (int) BlackCardinal,
\r
1369 PM_BD = (int) BlackDragon,
\r
1370 PM_BL = (int) BlackLance,
\r
1371 PM_BS = (int) BlackCobra,
\r
1372 PM_BV = (int) BlackFalcon,
\r
1373 PM_BSG = (int) BlackSilver,
\r
1374 PM_BK = (int) BlackKing
\r
1377 static HFONT hPieceFont = NULL;
\r
1378 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1379 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1380 static int fontBitmapSquareSize = 0;
\r
1381 static char pieceToFontChar[(int) EmptySquare] =
\r
1382 { 'p', 'n', 'b', 'r', 'q',
\r
1383 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1384 'k', 'o', 'm', 'v', 't', 'w',
\r
1385 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1388 extern BOOL SetCharTable( char *table, const char * map );
\r
1389 /* [HGM] moved to backend.c */
\r
1391 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1394 BYTE r1 = GetRValue( color );
\r
1395 BYTE g1 = GetGValue( color );
\r
1396 BYTE b1 = GetBValue( color );
\r
1402 /* Create a uniform background first */
\r
1403 hbrush = CreateSolidBrush( color );
\r
1404 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1405 FillRect( hdc, &rc, hbrush );
\r
1406 DeleteObject( hbrush );
\r
1409 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1410 int steps = squareSize / 2;
\r
1413 for( i=0; i<steps; i++ ) {
\r
1414 BYTE r = r1 - (r1-r2) * i / steps;
\r
1415 BYTE g = g1 - (g1-g2) * i / steps;
\r
1416 BYTE b = b1 - (b1-b2) * i / steps;
\r
1418 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1419 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1420 FillRect( hdc, &rc, hbrush );
\r
1421 DeleteObject(hbrush);
\r
1424 else if( mode == 2 ) {
\r
1425 /* Diagonal gradient, good more or less for every piece */
\r
1426 POINT triangle[3];
\r
1427 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1428 HBRUSH hbrush_old;
\r
1429 int steps = squareSize;
\r
1432 triangle[0].x = squareSize - steps;
\r
1433 triangle[0].y = squareSize;
\r
1434 triangle[1].x = squareSize;
\r
1435 triangle[1].y = squareSize;
\r
1436 triangle[2].x = squareSize;
\r
1437 triangle[2].y = squareSize - steps;
\r
1439 for( i=0; i<steps; i++ ) {
\r
1440 BYTE r = r1 - (r1-r2) * i / steps;
\r
1441 BYTE g = g1 - (g1-g2) * i / steps;
\r
1442 BYTE b = b1 - (b1-b2) * i / steps;
\r
1444 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1445 hbrush_old = SelectObject( hdc, hbrush );
\r
1446 Polygon( hdc, triangle, 3 );
\r
1447 SelectObject( hdc, hbrush_old );
\r
1448 DeleteObject(hbrush);
\r
1453 SelectObject( hdc, hpen );
\r
1458 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1459 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1460 piece: follow the steps as explained below.
\r
1462 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1466 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1470 int backColor = whitePieceColor;
\r
1471 int foreColor = blackPieceColor;
\r
1473 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1474 backColor = appData.fontBackColorWhite;
\r
1475 foreColor = appData.fontForeColorWhite;
\r
1477 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1478 backColor = appData.fontBackColorBlack;
\r
1479 foreColor = appData.fontForeColorBlack;
\r
1483 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1485 hbm_old = SelectObject( hdc, hbm );
\r
1489 rc.right = squareSize;
\r
1490 rc.bottom = squareSize;
\r
1492 /* Step 1: background is now black */
\r
1493 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1495 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1497 pt.x = (squareSize - sz.cx) / 2;
\r
1498 pt.y = (squareSize - sz.cy) / 2;
\r
1500 SetBkMode( hdc, TRANSPARENT );
\r
1501 SetTextColor( hdc, chroma );
\r
1502 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1503 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1505 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1506 /* Step 3: the area outside the piece is filled with white */
\r
1507 // FloodFill( hdc, 0, 0, chroma );
\r
1508 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1509 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1510 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1511 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1512 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1514 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1515 but if the start point is not inside the piece we're lost!
\r
1516 There should be a better way to do this... if we could create a region or path
\r
1517 from the fill operation we would be fine for example.
\r
1519 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1520 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1522 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1523 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1524 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1526 SelectObject( dc2, bm2 );
\r
1527 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1528 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1529 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1530 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1531 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1534 DeleteObject( bm2 );
\r
1537 SetTextColor( hdc, 0 );
\r
1539 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1540 draw the piece again in black for safety.
\r
1542 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1544 SelectObject( hdc, hbm_old );
\r
1546 if( hPieceMask[index] != NULL ) {
\r
1547 DeleteObject( hPieceMask[index] );
\r
1550 hPieceMask[index] = hbm;
\r
1553 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1555 SelectObject( hdc, hbm );
\r
1558 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1559 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1560 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1562 SelectObject( dc1, hPieceMask[index] );
\r
1563 SelectObject( dc2, bm2 );
\r
1564 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1565 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1568 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1569 the piece background and deletes (makes transparent) the rest.
\r
1570 Thanks to that mask, we are free to paint the background with the greates
\r
1571 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1572 We use this, to make gradients and give the pieces a "roundish" look.
\r
1574 SetPieceBackground( hdc, backColor, 2 );
\r
1575 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1579 DeleteObject( bm2 );
\r
1582 SetTextColor( hdc, foreColor );
\r
1583 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1585 SelectObject( hdc, hbm_old );
\r
1587 if( hPieceFace[index] != NULL ) {
\r
1588 DeleteObject( hPieceFace[index] );
\r
1591 hPieceFace[index] = hbm;
\r
1594 static int TranslatePieceToFontPiece( int piece )
\r
1624 case BlackMarshall:
\r
1628 case BlackNightrider:
\r
1634 case BlackUnicorn:
\r
1638 case BlackGrasshopper:
\r
1650 case BlackCardinal:
\r
1657 case WhiteMarshall:
\r
1661 case WhiteNightrider:
\r
1667 case WhiteUnicorn:
\r
1671 case WhiteGrasshopper:
\r
1683 case WhiteCardinal:
\r
1692 void CreatePiecesFromFont()
\r
1695 HDC hdc_window = NULL;
\r
1701 if( fontBitmapSquareSize < 0 ) {
\r
1702 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1706 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1707 fontBitmapSquareSize = -1;
\r
1711 if( fontBitmapSquareSize != squareSize ) {
\r
1712 hdc_window = GetDC( hwndMain );
\r
1713 hdc = CreateCompatibleDC( hdc_window );
\r
1715 if( hPieceFont != NULL ) {
\r
1716 DeleteObject( hPieceFont );
\r
1719 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1720 hPieceMask[i] = NULL;
\r
1721 hPieceFace[i] = NULL;
\r
1727 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1728 fontHeight = appData.fontPieceSize;
\r
1731 fontHeight = (fontHeight * squareSize) / 100;
\r
1733 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1735 lf.lfEscapement = 0;
\r
1736 lf.lfOrientation = 0;
\r
1737 lf.lfWeight = FW_NORMAL;
\r
1739 lf.lfUnderline = 0;
\r
1740 lf.lfStrikeOut = 0;
\r
1741 lf.lfCharSet = DEFAULT_CHARSET;
\r
1742 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1743 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1744 lf.lfQuality = PROOF_QUALITY;
\r
1745 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1746 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1747 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1749 hPieceFont = CreateFontIndirect( &lf );
\r
1751 if( hPieceFont == NULL ) {
\r
1752 fontBitmapSquareSize = -2;
\r
1755 /* Setup font-to-piece character table */
\r
1756 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1757 /* No (or wrong) global settings, try to detect the font */
\r
1758 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1760 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1762 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1763 /* DiagramTT* family */
\r
1764 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1766 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1767 /* Fairy symbols */
\r
1768 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1770 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1771 /* Good Companion (Some characters get warped as literal :-( */
\r
1772 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1773 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1774 SetCharTable(pieceToFontChar, s);
\r
1777 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1778 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1782 /* Create bitmaps */
\r
1783 hfont_old = SelectObject( hdc, hPieceFont );
\r
1784 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1785 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1786 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1788 SelectObject( hdc, hfont_old );
\r
1790 fontBitmapSquareSize = squareSize;
\r
1794 if( hdc != NULL ) {
\r
1798 if( hdc_window != NULL ) {
\r
1799 ReleaseDC( hwndMain, hdc_window );
\r
1804 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1808 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1809 if (gameInfo.event &&
\r
1810 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1811 strcmp(name, "k80s") == 0) {
\r
1812 strcpy(name, "tim");
\r
1814 return LoadBitmap(hinst, name);
\r
1818 /* Insert a color into the program's logical palette
\r
1819 structure. This code assumes the given color is
\r
1820 the result of the RGB or PALETTERGB macro, and it
\r
1821 knows how those macros work (which is documented).
\r
1824 InsertInPalette(COLORREF color)
\r
1826 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1828 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1829 DisplayFatalError("Too many colors", 0, 1);
\r
1830 pLogPal->palNumEntries--;
\r
1834 pe->peFlags = (char) 0;
\r
1835 pe->peRed = (char) (0xFF & color);
\r
1836 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1837 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1843 InitDrawingColors()
\r
1845 if (pLogPal == NULL) {
\r
1846 /* Allocate enough memory for a logical palette with
\r
1847 * PALETTESIZE entries and set the size and version fields
\r
1848 * of the logical palette structure.
\r
1850 pLogPal = (NPLOGPALETTE)
\r
1851 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1852 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1853 pLogPal->palVersion = 0x300;
\r
1855 pLogPal->palNumEntries = 0;
\r
1857 InsertInPalette(lightSquareColor);
\r
1858 InsertInPalette(darkSquareColor);
\r
1859 InsertInPalette(whitePieceColor);
\r
1860 InsertInPalette(blackPieceColor);
\r
1861 InsertInPalette(highlightSquareColor);
\r
1862 InsertInPalette(premoveHighlightColor);
\r
1864 /* create a logical color palette according the information
\r
1865 * in the LOGPALETTE structure.
\r
1867 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1869 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1870 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1871 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1872 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1873 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1874 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1875 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1876 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1877 /* [AS] Force rendering of the font-based pieces */
\r
1878 if( fontBitmapSquareSize > 0 ) {
\r
1879 fontBitmapSquareSize = 0;
\r
1885 BoardWidth(int boardSize, int n)
\r
1886 { /* [HGM] argument n added to allow different width and height */
\r
1887 int lineGap = sizeInfo[boardSize].lineGap;
\r
1889 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1890 lineGap = appData.overrideLineGap;
\r
1893 return (n + 1) * lineGap +
\r
1894 n * sizeInfo[boardSize].squareSize;
\r
1897 /* Respond to board resize by dragging edge */
\r
1899 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1901 BoardSize newSize = NUM_SIZES - 1;
\r
1902 static int recurse = 0;
\r
1903 if (IsIconic(hwndMain)) return;
\r
1904 if (recurse > 0) return;
\r
1906 while (newSize > 0) {
\r
1907 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1908 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1909 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1912 boardSize = newSize;
\r
1913 InitDrawingSizes(boardSize, flags);
\r
1918 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1921 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1923 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1924 ChessSquare piece;
\r
1925 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1927 SIZE clockSize, messageSize;
\r
1929 char buf[MSG_SIZ];
\r
1931 HMENU hmenu = GetMenu(hwndMain);
\r
1932 RECT crect, wrect, oldRect;
\r
1934 LOGBRUSH logbrush;
\r
1936 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1937 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1939 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1940 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1942 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1943 oldRect.top = wpMain.y;
\r
1944 oldRect.right = wpMain.x + wpMain.width;
\r
1945 oldRect.bottom = wpMain.y + wpMain.height;
\r
1947 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1948 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1949 squareSize = sizeInfo[boardSize].squareSize;
\r
1950 lineGap = sizeInfo[boardSize].lineGap;
\r
1951 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1953 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1954 lineGap = appData.overrideLineGap;
\r
1957 if (tinyLayout != oldTinyLayout) {
\r
1958 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1960 style &= ~WS_SYSMENU;
\r
1961 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1962 "&Minimize\tCtrl+F4");
\r
1964 style |= WS_SYSMENU;
\r
1965 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1967 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1969 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1970 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1971 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1973 DrawMenuBar(hwndMain);
\r
1976 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1977 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1979 /* Get text area sizes */
\r
1980 hdc = GetDC(hwndMain);
\r
1981 if (appData.clockMode) {
\r
1982 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1984 sprintf(buf, "White");
\r
1986 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1987 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1988 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1989 str = "We only care about the height here";
\r
1990 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1991 SelectObject(hdc, oldFont);
\r
1992 ReleaseDC(hwndMain, hdc);
\r
1994 /* Compute where everything goes */
\r
1995 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1996 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1997 logoHeight = 2*clockSize.cy;
\r
1998 leftLogoRect.left = OUTER_MARGIN;
\r
1999 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2000 leftLogoRect.top = OUTER_MARGIN;
\r
2001 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2003 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2004 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2005 rightLogoRect.top = OUTER_MARGIN;
\r
2006 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2009 whiteRect.left = leftLogoRect.right;
\r
2010 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2011 whiteRect.top = OUTER_MARGIN;
\r
2012 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2014 blackRect.right = rightLogoRect.left;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.top = whiteRect.top;
\r
2017 blackRect.bottom = whiteRect.bottom;
\r
2019 whiteRect.left = OUTER_MARGIN;
\r
2020 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2021 whiteRect.top = OUTER_MARGIN;
\r
2022 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2024 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2025 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2026 blackRect.top = whiteRect.top;
\r
2027 blackRect.bottom = whiteRect.bottom;
\r
2029 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2032 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2033 if (appData.showButtonBar) {
\r
2034 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2035 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2037 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2039 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2040 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2042 boardRect.left = OUTER_MARGIN;
\r
2043 boardRect.right = boardRect.left + boardWidth;
\r
2044 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2045 boardRect.bottom = boardRect.top + boardHeight;
\r
2047 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2048 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2049 oldBoardSize = boardSize;
\r
2050 oldTinyLayout = tinyLayout;
\r
2051 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2052 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2053 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2054 winW *= 1 + twoBoards;
\r
2055 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2056 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2057 wpMain.height = winH; // without disturbing window attachments
\r
2058 GetWindowRect(hwndMain, &wrect);
\r
2059 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2060 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2062 // [HGM] placement: let attached windows follow size change.
\r
2063 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2064 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2065 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2066 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2067 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2069 /* compensate if menu bar wrapped */
\r
2070 GetClientRect(hwndMain, &crect);
\r
2071 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2072 wpMain.height += offby;
\r
2074 case WMSZ_TOPLEFT:
\r
2075 SetWindowPos(hwndMain, NULL,
\r
2076 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2077 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2080 case WMSZ_TOPRIGHT:
\r
2082 SetWindowPos(hwndMain, NULL,
\r
2083 wrect.left, wrect.bottom - wpMain.height,
\r
2084 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2087 case WMSZ_BOTTOMLEFT:
\r
2089 SetWindowPos(hwndMain, NULL,
\r
2090 wrect.right - wpMain.width, wrect.top,
\r
2091 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2094 case WMSZ_BOTTOMRIGHT:
\r
2098 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2099 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2104 for (i = 0; i < N_BUTTONS; i++) {
\r
2105 if (buttonDesc[i].hwnd != NULL) {
\r
2106 DestroyWindow(buttonDesc[i].hwnd);
\r
2107 buttonDesc[i].hwnd = NULL;
\r
2109 if (appData.showButtonBar) {
\r
2110 buttonDesc[i].hwnd =
\r
2111 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2112 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2113 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2114 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2115 (HMENU) buttonDesc[i].id,
\r
2116 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2118 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2119 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2120 MAKELPARAM(FALSE, 0));
\r
2122 if (buttonDesc[i].id == IDM_Pause)
\r
2123 hwndPause = buttonDesc[i].hwnd;
\r
2124 buttonDesc[i].wndproc = (WNDPROC)
\r
2125 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2128 if (gridPen != NULL) DeleteObject(gridPen);
\r
2129 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2130 if (premovePen != NULL) DeleteObject(premovePen);
\r
2131 if (lineGap != 0) {
\r
2132 logbrush.lbStyle = BS_SOLID;
\r
2133 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2135 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2136 lineGap, &logbrush, 0, NULL);
\r
2137 logbrush.lbColor = highlightSquareColor;
\r
2139 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2140 lineGap, &logbrush, 0, NULL);
\r
2142 logbrush.lbColor = premoveHighlightColor;
\r
2144 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2145 lineGap, &logbrush, 0, NULL);
\r
2147 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2148 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2149 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2150 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2151 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2152 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2153 BOARD_WIDTH * (squareSize + lineGap);
\r
2154 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2156 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2157 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2158 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2159 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2160 lineGap / 2 + (i * (squareSize + lineGap));
\r
2161 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2162 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2163 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2167 /* [HGM] Licensing requirement */
\r
2169 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2172 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2174 GothicPopUp( "", VariantNormal);
\r
2177 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2179 /* Load piece bitmaps for this board size */
\r
2180 for (i=0; i<=2; i++) {
\r
2181 for (piece = WhitePawn;
\r
2182 (int) piece < (int) BlackPawn;
\r
2183 piece = (ChessSquare) ((int) piece + 1)) {
\r
2184 if (pieceBitmap[i][piece] != NULL)
\r
2185 DeleteObject(pieceBitmap[i][piece]);
\r
2189 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2190 // Orthodox Chess pieces
\r
2191 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2192 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2193 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2194 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2195 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2196 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2197 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2198 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2199 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2200 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2201 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2202 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2203 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2204 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2205 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2206 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2207 // in Shogi, Hijack the unused Queen for Lance
\r
2208 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2209 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2210 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2212 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2217 if(squareSize <= 72 && squareSize >= 33) {
\r
2218 /* A & C are available in most sizes now */
\r
2219 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2220 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2221 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2222 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2223 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2224 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2225 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2226 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2227 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2228 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2229 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2232 } else { // Smirf-like
\r
2233 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2237 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2238 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2241 } else { // WinBoard standard
\r
2242 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2249 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2250 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2259 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2271 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2272 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2273 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2281 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2282 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2285 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2286 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2287 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2288 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2289 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2290 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2291 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2292 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2293 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2295 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2296 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2297 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2298 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2299 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2300 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2301 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2302 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2303 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2304 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2305 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2306 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2309 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2310 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2311 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2312 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2313 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2314 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2315 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2316 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2317 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2318 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2319 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2320 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2321 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2322 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2323 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2327 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2328 /* special Shogi support in this size */
\r
2329 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2330 for (piece = WhitePawn;
\r
2331 (int) piece < (int) BlackPawn;
\r
2332 piece = (ChessSquare) ((int) piece + 1)) {
\r
2333 if (pieceBitmap[i][piece] != NULL)
\r
2334 DeleteObject(pieceBitmap[i][piece]);
\r
2337 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2342 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2344 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2345 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2346 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2347 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2348 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2349 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2350 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2351 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2356 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2358 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2359 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2360 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2361 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2362 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2363 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2364 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2365 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2370 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2372 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2373 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2374 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2375 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2376 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2377 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2378 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2384 PieceBitmap(ChessSquare p, int kind)
\r
2386 if ((int) p >= (int) BlackPawn)
\r
2387 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2389 return pieceBitmap[kind][(int) p];
\r
2392 /***************************************************************/
\r
2394 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2395 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2397 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2398 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2402 SquareToPos(int row, int column, int * x, int * y)
\r
2405 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2406 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2408 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2409 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2414 DrawCoordsOnDC(HDC hdc)
\r
2416 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
2417 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
2418 char str[2] = { NULLCHAR, NULLCHAR };
\r
2419 int oldMode, oldAlign, x, y, start, i;
\r
2423 if (!appData.showCoords)
\r
2426 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2428 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2429 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2430 oldAlign = GetTextAlign(hdc);
\r
2431 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2433 y = boardRect.top + lineGap;
\r
2434 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2436 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2437 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2438 str[0] = files[start + i];
\r
2439 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2440 y += squareSize + lineGap;
\r
2443 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2445 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2446 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2447 str[0] = ranks[start + i];
\r
2448 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2449 x += squareSize + lineGap;
\r
2452 SelectObject(hdc, oldBrush);
\r
2453 SetBkMode(hdc, oldMode);
\r
2454 SetTextAlign(hdc, oldAlign);
\r
2455 SelectObject(hdc, oldFont);
\r
2459 DrawGridOnDC(HDC hdc)
\r
2463 if (lineGap != 0) {
\r
2464 oldPen = SelectObject(hdc, gridPen);
\r
2465 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2466 SelectObject(hdc, oldPen);
\r
2470 #define HIGHLIGHT_PEN 0
\r
2471 #define PREMOVE_PEN 1
\r
2474 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2477 HPEN oldPen, hPen;
\r
2478 if (lineGap == 0) return;
\r
2480 x1 = boardRect.left +
\r
2481 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2482 y1 = boardRect.top +
\r
2483 lineGap/2 + y * (squareSize + lineGap);
\r
2485 x1 = boardRect.left +
\r
2486 lineGap/2 + x * (squareSize + lineGap);
\r
2487 y1 = boardRect.top +
\r
2488 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2490 hPen = pen ? premovePen : highlightPen;
\r
2491 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2492 MoveToEx(hdc, x1, y1, NULL);
\r
2493 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2494 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2495 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2496 LineTo(hdc, x1, y1);
\r
2497 SelectObject(hdc, oldPen);
\r
2501 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2504 for (i=0; i<2; i++) {
\r
2505 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2506 DrawHighlightOnDC(hdc, TRUE,
\r
2507 h->sq[i].x, h->sq[i].y,
\r
2512 /* Note: sqcolor is used only in monoMode */
\r
2513 /* Note that this code is largely duplicated in woptions.c,
\r
2514 function DrawSampleSquare, so that needs to be updated too */
\r
2516 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2518 HBITMAP oldBitmap;
\r
2522 if (appData.blindfold) return;
\r
2524 /* [AS] Use font-based pieces if needed */
\r
2525 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2526 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2527 CreatePiecesFromFont();
\r
2529 if( fontBitmapSquareSize == squareSize ) {
\r
2530 int index = TranslatePieceToFontPiece(piece);
\r
2532 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2534 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2535 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2539 squareSize, squareSize,
\r
2544 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2546 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2547 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2551 squareSize, squareSize,
\r
2560 if (appData.monoMode) {
\r
2561 SelectObject(tmphdc, PieceBitmap(piece,
\r
2562 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2563 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2564 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2566 tmpSize = squareSize;
\r
2568 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2569 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2570 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2571 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2572 x += (squareSize - minorSize)>>1;
\r
2573 y += squareSize - minorSize - 2;
\r
2574 tmpSize = minorSize;
\r
2576 if (color || appData.allWhite ) {
\r
2577 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2579 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2580 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2581 if(appData.upsideDown && color==flipView)
\r
2582 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2584 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2585 /* Use black for outline of white pieces */
\r
2586 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2587 if(appData.upsideDown && color==flipView)
\r
2588 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2590 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2592 /* Use square color for details of black pieces */
\r
2593 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2594 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2595 if(appData.upsideDown && !flipView)
\r
2596 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2598 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2600 SelectObject(hdc, oldBrush);
\r
2601 SelectObject(tmphdc, oldBitmap);
\r
2605 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2606 int GetBackTextureMode( int algo )
\r
2608 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2612 case BACK_TEXTURE_MODE_PLAIN:
\r
2613 result = 1; /* Always use identity map */
\r
2615 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2616 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2624 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2625 to handle redraws cleanly (as random numbers would always be different).
\r
2627 VOID RebuildTextureSquareInfo()
\r
2637 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2639 if( liteBackTexture != NULL ) {
\r
2640 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2641 lite_w = bi.bmWidth;
\r
2642 lite_h = bi.bmHeight;
\r
2646 if( darkBackTexture != NULL ) {
\r
2647 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2648 dark_w = bi.bmWidth;
\r
2649 dark_h = bi.bmHeight;
\r
2653 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2654 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2655 if( (col + row) & 1 ) {
\r
2657 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2658 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2659 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2661 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2662 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2663 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2665 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2666 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2671 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2672 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2673 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2675 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2676 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2677 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2679 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2680 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2687 /* [AS] Arrow highlighting support */
\r
2689 static int A_WIDTH = 5; /* Width of arrow body */
\r
2691 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2692 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2694 static double Sqr( double x )
\r
2699 static int Round( double x )
\r
2701 return (int) (x + 0.5);
\r
2704 /* Draw an arrow between two points using current settings */
\r
2705 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2708 double dx, dy, j, k, x, y;
\r
2710 if( d_x == s_x ) {
\r
2711 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2713 arrow[0].x = s_x + A_WIDTH;
\r
2716 arrow[1].x = s_x + A_WIDTH;
\r
2717 arrow[1].y = d_y - h;
\r
2719 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2720 arrow[2].y = d_y - h;
\r
2725 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2726 arrow[4].y = d_y - h;
\r
2728 arrow[5].x = s_x - A_WIDTH;
\r
2729 arrow[5].y = d_y - h;
\r
2731 arrow[6].x = s_x - A_WIDTH;
\r
2734 else if( d_y == s_y ) {
\r
2735 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2738 arrow[0].y = s_y + A_WIDTH;
\r
2740 arrow[1].x = d_x - w;
\r
2741 arrow[1].y = s_y + A_WIDTH;
\r
2743 arrow[2].x = d_x - w;
\r
2744 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2749 arrow[4].x = d_x - w;
\r
2750 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2752 arrow[5].x = d_x - w;
\r
2753 arrow[5].y = s_y - A_WIDTH;
\r
2756 arrow[6].y = s_y - A_WIDTH;
\r
2759 /* [AS] Needed a lot of paper for this! :-) */
\r
2760 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2761 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2763 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2765 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2770 arrow[0].x = Round(x - j);
\r
2771 arrow[0].y = Round(y + j*dx);
\r
2773 arrow[1].x = Round(x + j);
\r
2774 arrow[1].y = Round(y - j*dx);
\r
2777 x = (double) d_x - k;
\r
2778 y = (double) d_y - k*dy;
\r
2781 x = (double) d_x + k;
\r
2782 y = (double) d_y + k*dy;
\r
2785 arrow[2].x = Round(x + j);
\r
2786 arrow[2].y = Round(y - j*dx);
\r
2788 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2789 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2794 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2795 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2797 arrow[6].x = Round(x - j);
\r
2798 arrow[6].y = Round(y + j*dx);
\r
2801 Polygon( hdc, arrow, 7 );
\r
2804 /* [AS] Draw an arrow between two squares */
\r
2805 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2807 int s_x, s_y, d_x, d_y;
\r
2814 if( s_col == d_col && s_row == d_row ) {
\r
2818 /* Get source and destination points */
\r
2819 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2820 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2823 d_y += squareSize / 4;
\r
2825 else if( d_y < s_y ) {
\r
2826 d_y += 3 * squareSize / 4;
\r
2829 d_y += squareSize / 2;
\r
2833 d_x += squareSize / 4;
\r
2835 else if( d_x < s_x ) {
\r
2836 d_x += 3 * squareSize / 4;
\r
2839 d_x += squareSize / 2;
\r
2842 s_x += squareSize / 2;
\r
2843 s_y += squareSize / 2;
\r
2845 /* Adjust width */
\r
2846 A_WIDTH = squareSize / 14;
\r
2849 stLB.lbStyle = BS_SOLID;
\r
2850 stLB.lbColor = appData.highlightArrowColor;
\r
2853 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2854 holdpen = SelectObject( hdc, hpen );
\r
2855 hbrush = CreateBrushIndirect( &stLB );
\r
2856 holdbrush = SelectObject( hdc, hbrush );
\r
2858 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2860 SelectObject( hdc, holdpen );
\r
2861 SelectObject( hdc, holdbrush );
\r
2862 DeleteObject( hpen );
\r
2863 DeleteObject( hbrush );
\r
2866 BOOL HasHighlightInfo()
\r
2868 BOOL result = FALSE;
\r
2870 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2871 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2879 BOOL IsDrawArrowEnabled()
\r
2881 BOOL result = FALSE;
\r
2883 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2890 VOID DrawArrowHighlight( HDC hdc )
\r
2892 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2893 DrawArrowBetweenSquares( hdc,
\r
2894 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2895 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2899 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2901 HRGN result = NULL;
\r
2903 if( HasHighlightInfo() ) {
\r
2904 int x1, y1, x2, y2;
\r
2905 int sx, sy, dx, dy;
\r
2907 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2908 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2910 sx = MIN( x1, x2 );
\r
2911 sy = MIN( y1, y2 );
\r
2912 dx = MAX( x1, x2 ) + squareSize;
\r
2913 dy = MAX( y1, y2 ) + squareSize;
\r
2915 result = CreateRectRgn( sx, sy, dx, dy );
\r
2922 Warning: this function modifies the behavior of several other functions.
\r
2924 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2925 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2926 repaint is scattered all over the place, which is not good for features such as
\r
2927 "arrow highlighting" that require a full repaint of the board.
\r
2929 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2930 user interaction, when speed is not so important) but especially to avoid errors
\r
2931 in the displayed graphics.
\r
2933 In such patched places, I always try refer to this function so there is a single
\r
2934 place to maintain knowledge.
\r
2936 To restore the original behavior, just return FALSE unconditionally.
\r
2938 BOOL IsFullRepaintPreferrable()
\r
2940 BOOL result = FALSE;
\r
2942 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2943 /* Arrow may appear on the board */
\r
2951 This function is called by DrawPosition to know whether a full repaint must
\r
2954 Only DrawPosition may directly call this function, which makes use of
\r
2955 some state information. Other function should call DrawPosition specifying
\r
2956 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2958 BOOL DrawPositionNeedsFullRepaint()
\r
2960 BOOL result = FALSE;
\r
2963 Probably a slightly better policy would be to trigger a full repaint
\r
2964 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2965 but animation is fast enough that it's difficult to notice.
\r
2967 if( animInfo.piece == EmptySquare ) {
\r
2968 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2977 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2979 int row, column, x, y, square_color, piece_color;
\r
2980 ChessSquare piece;
\r
2982 HDC texture_hdc = NULL;
\r
2984 /* [AS] Initialize background textures if needed */
\r
2985 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2986 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2987 if( backTextureSquareSize != squareSize
\r
2988 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2989 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2990 backTextureSquareSize = squareSize;
\r
2991 RebuildTextureSquareInfo();
\r
2994 texture_hdc = CreateCompatibleDC( hdc );
\r
2997 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2998 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3000 SquareToPos(row, column, &x, &y);
\r
3002 piece = board[row][column];
\r
3004 square_color = ((column + row) % 2) == 1;
\r
3005 if( gameInfo.variant == VariantXiangqi ) {
\r
3006 square_color = !InPalace(row, column);
\r
3007 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3008 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3010 piece_color = (int) piece < (int) BlackPawn;
\r
3013 /* [HGM] holdings file: light square or black */
\r
3014 if(column == BOARD_LEFT-2) {
\r
3015 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3018 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3022 if(column == BOARD_RGHT + 1 ) {
\r
3023 if( row < gameInfo.holdingsSize )
\r
3026 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3030 if(column == BOARD_LEFT-1 ) /* left align */
\r
3031 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3032 else if( column == BOARD_RGHT) /* right align */
\r
3033 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3035 if (appData.monoMode) {
\r
3036 if (piece == EmptySquare) {
\r
3037 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3038 square_color ? WHITENESS : BLACKNESS);
\r
3040 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3043 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3044 /* [AS] Draw the square using a texture bitmap */
\r
3045 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3046 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3047 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3050 squareSize, squareSize,
\r
3053 backTextureSquareInfo[r][c].mode,
\r
3054 backTextureSquareInfo[r][c].x,
\r
3055 backTextureSquareInfo[r][c].y );
\r
3057 SelectObject( texture_hdc, hbm );
\r
3059 if (piece != EmptySquare) {
\r
3060 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3064 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3066 oldBrush = SelectObject(hdc, brush );
\r
3067 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3068 SelectObject(hdc, oldBrush);
\r
3069 if (piece != EmptySquare)
\r
3070 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3075 if( texture_hdc != NULL ) {
\r
3076 DeleteDC( texture_hdc );
\r
3080 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3081 void fputDW(FILE *f, int x)
\r
3083 fputc(x & 255, f);
\r
3084 fputc(x>>8 & 255, f);
\r
3085 fputc(x>>16 & 255, f);
\r
3086 fputc(x>>24 & 255, f);
\r
3089 #define MAX_CLIPS 200 /* more than enough */
\r
3092 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3094 // HBITMAP bufferBitmap;
\r
3099 int w = 100, h = 50;
\r
3101 if(logo == NULL) return;
\r
3102 // GetClientRect(hwndMain, &Rect);
\r
3103 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3104 // Rect.bottom-Rect.top+1);
\r
3105 tmphdc = CreateCompatibleDC(hdc);
\r
3106 hbm = SelectObject(tmphdc, logo);
\r
3107 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3111 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3112 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3113 SelectObject(tmphdc, hbm);
\r
3117 static HDC hdcSeek;
\r
3119 // [HGM] seekgraph
\r
3120 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3123 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3124 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3125 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3126 SelectObject( hdcSeek, hp );
\r
3129 // front-end wrapper for drawing functions to do rectangles
\r
3130 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3135 if (hdcSeek == NULL) {
\r
3136 hdcSeek = GetDC(hwndMain);
\r
3137 if (!appData.monoMode) {
\r
3138 SelectPalette(hdcSeek, hPal, FALSE);
\r
3139 RealizePalette(hdcSeek);
\r
3142 hp = SelectObject( hdcSeek, gridPen );
\r
3143 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3144 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3145 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3146 SelectObject( hdcSeek, hp );
\r
3149 // front-end wrapper for putting text in graph
\r
3150 void DrawSeekText(char *buf, int x, int y)
\r
3153 SetBkMode( hdcSeek, TRANSPARENT );
\r
3154 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3155 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3158 void DrawSeekDot(int x, int y, int color)
\r
3160 int square = color & 0x80;
\r
3162 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3163 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3165 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3166 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3168 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3169 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3170 SelectObject(hdcSeek, oldBrush);
\r
3174 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3176 static Board lastReq[2], lastDrawn[2];
\r
3177 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3178 static int lastDrawnFlipView = 0;
\r
3179 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3180 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3183 HBITMAP bufferBitmap;
\r
3184 HBITMAP oldBitmap;
\r
3186 HRGN clips[MAX_CLIPS];
\r
3187 ChessSquare dragged_piece = EmptySquare;
\r
3188 int nr = twoBoards*partnerUp;
\r
3190 /* I'm undecided on this - this function figures out whether a full
\r
3191 * repaint is necessary on its own, so there's no real reason to have the
\r
3192 * caller tell it that. I think this can safely be set to FALSE - but
\r
3193 * if we trust the callers not to request full repaints unnessesarily, then
\r
3194 * we could skip some clipping work. In other words, only request a full
\r
3195 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3196 * gamestart and similar) --Hawk
\r
3198 Boolean fullrepaint = repaint;
\r
3200 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3202 if( DrawPositionNeedsFullRepaint() ) {
\r
3203 fullrepaint = TRUE;
\r
3206 if (board == NULL) {
\r
3207 if (!lastReqValid[nr]) {
\r
3210 board = lastReq[nr];
\r
3212 CopyBoard(lastReq[nr], board);
\r
3213 lastReqValid[nr] = 1;
\r
3216 if (doingSizing) {
\r
3220 if (IsIconic(hwndMain)) {
\r
3224 if (hdc == NULL) {
\r
3225 hdc = GetDC(hwndMain);
\r
3226 if (!appData.monoMode) {
\r
3227 SelectPalette(hdc, hPal, FALSE);
\r
3228 RealizePalette(hdc);
\r
3232 releaseDC = FALSE;
\r
3235 /* Create some work-DCs */
\r
3236 hdcmem = CreateCompatibleDC(hdc);
\r
3237 tmphdc = CreateCompatibleDC(hdc);
\r
3239 /* If dragging is in progress, we temporarely remove the piece */
\r
3240 /* [HGM] or temporarily decrease count if stacked */
\r
3241 /* !! Moved to before board compare !! */
\r
3242 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3243 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3244 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3245 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3246 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3248 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3249 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3250 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3252 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3255 /* Figure out which squares need updating by comparing the
\r
3256 * newest board with the last drawn board and checking if
\r
3257 * flipping has changed.
\r
3259 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3260 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3261 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3262 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3263 SquareToPos(row, column, &x, &y);
\r
3264 clips[num_clips++] =
\r
3265 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3269 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3270 for (i=0; i<2; i++) {
\r
3271 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3272 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3273 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3274 lastDrawnHighlight.sq[i].y >= 0) {
\r
3275 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3276 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3277 clips[num_clips++] =
\r
3278 CreateRectRgn(x - lineGap, y - lineGap,
\r
3279 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3281 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3282 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3283 clips[num_clips++] =
\r
3284 CreateRectRgn(x - lineGap, y - lineGap,
\r
3285 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3289 for (i=0; i<2; i++) {
\r
3290 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3291 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3292 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3293 lastDrawnPremove.sq[i].y >= 0) {
\r
3294 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3295 lastDrawnPremove.sq[i].x, &x, &y);
\r
3296 clips[num_clips++] =
\r
3297 CreateRectRgn(x - lineGap, y - lineGap,
\r
3298 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3300 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3301 premoveHighlightInfo.sq[i].y >= 0) {
\r
3302 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3303 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3304 clips[num_clips++] =
\r
3305 CreateRectRgn(x - lineGap, y - lineGap,
\r
3306 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3310 } else { // nr == 1
\r
3311 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3312 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3313 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3314 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3315 for (i=0; i<2; i++) {
\r
3316 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3317 partnerHighlightInfo.sq[i].y >= 0) {
\r
3318 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3319 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3320 clips[num_clips++] =
\r
3321 CreateRectRgn(x - lineGap, y - lineGap,
\r
3322 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3324 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3325 oldPartnerHighlight.sq[i].y >= 0) {
\r
3326 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3327 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3328 clips[num_clips++] =
\r
3329 CreateRectRgn(x - lineGap, y - lineGap,
\r
3330 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3335 fullrepaint = TRUE;
\r
3338 /* Create a buffer bitmap - this is the actual bitmap
\r
3339 * being written to. When all the work is done, we can
\r
3340 * copy it to the real DC (the screen). This avoids
\r
3341 * the problems with flickering.
\r
3343 GetClientRect(hwndMain, &Rect);
\r
3344 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3345 Rect.bottom-Rect.top+1);
\r
3346 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3347 if (!appData.monoMode) {
\r
3348 SelectPalette(hdcmem, hPal, FALSE);
\r
3351 /* Create clips for dragging */
\r
3352 if (!fullrepaint) {
\r
3353 if (dragInfo.from.x >= 0) {
\r
3354 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3355 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3357 if (dragInfo.start.x >= 0) {
\r
3358 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3359 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3361 if (dragInfo.pos.x >= 0) {
\r
3362 x = dragInfo.pos.x - squareSize / 2;
\r
3363 y = dragInfo.pos.y - squareSize / 2;
\r
3364 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3366 if (dragInfo.lastpos.x >= 0) {
\r
3367 x = dragInfo.lastpos.x - squareSize / 2;
\r
3368 y = dragInfo.lastpos.y - squareSize / 2;
\r
3369 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3373 /* Are we animating a move?
\r
3375 * - remove the piece from the board (temporarely)
\r
3376 * - calculate the clipping region
\r
3378 if (!fullrepaint) {
\r
3379 if (animInfo.piece != EmptySquare) {
\r
3380 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3381 x = boardRect.left + animInfo.lastpos.x;
\r
3382 y = boardRect.top + animInfo.lastpos.y;
\r
3383 x2 = boardRect.left + animInfo.pos.x;
\r
3384 y2 = boardRect.top + animInfo.pos.y;
\r
3385 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3386 /* Slight kludge. The real problem is that after AnimateMove is
\r
3387 done, the position on the screen does not match lastDrawn.
\r
3388 This currently causes trouble only on e.p. captures in
\r
3389 atomic, where the piece moves to an empty square and then
\r
3390 explodes. The old and new positions both had an empty square
\r
3391 at the destination, but animation has drawn a piece there and
\r
3392 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3393 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3397 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3398 if (num_clips == 0)
\r
3399 fullrepaint = TRUE;
\r
3401 /* Set clipping on the memory DC */
\r
3402 if (!fullrepaint) {
\r
3403 SelectClipRgn(hdcmem, clips[0]);
\r
3404 for (x = 1; x < num_clips; x++) {
\r
3405 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3406 abort(); // this should never ever happen!
\r
3410 /* Do all the drawing to the memory DC */
\r
3411 if(explodeInfo.radius) { // [HGM] atomic
\r
3413 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3414 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3415 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3416 x += squareSize/2;
\r
3417 y += squareSize/2;
\r
3418 if(!fullrepaint) {
\r
3419 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3420 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3422 DrawGridOnDC(hdcmem);
\r
3423 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3424 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3425 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3426 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3427 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3428 SelectObject(hdcmem, oldBrush);
\r
3430 DrawGridOnDC(hdcmem);
\r
3431 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3432 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3433 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3435 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3436 oldPartnerHighlight = partnerHighlightInfo;
\r
3438 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3440 if(nr == 0) // [HGM] dual: markers only on left board
\r
3441 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3442 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3443 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3444 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3445 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3446 SquareToPos(row, column, &x, &y);
\r
3447 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3448 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3449 SelectObject(hdcmem, oldBrush);
\r
3454 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3455 if(appData.autoLogo) {
\r
3457 switch(gameMode) { // pick logos based on game mode
\r
3458 case IcsObserving:
\r
3459 whiteLogo = second.programLogo; // ICS logo
\r
3460 blackLogo = second.programLogo;
\r
3463 case IcsPlayingWhite:
\r
3464 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3465 blackLogo = second.programLogo; // ICS logo
\r
3467 case IcsPlayingBlack:
\r
3468 whiteLogo = second.programLogo; // ICS logo
\r
3469 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3471 case TwoMachinesPlay:
\r
3472 if(first.twoMachinesColor[0] == 'b') {
\r
3473 whiteLogo = second.programLogo;
\r
3474 blackLogo = first.programLogo;
\r
3477 case MachinePlaysWhite:
\r
3478 blackLogo = userLogo;
\r
3480 case MachinePlaysBlack:
\r
3481 whiteLogo = userLogo;
\r
3482 blackLogo = first.programLogo;
\r
3485 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3486 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3489 if( appData.highlightMoveWithArrow ) {
\r
3490 DrawArrowHighlight(hdcmem);
\r
3493 DrawCoordsOnDC(hdcmem);
\r
3495 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3496 /* to make sure lastDrawn contains what is actually drawn */
\r
3498 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3499 if (dragged_piece != EmptySquare) {
\r
3500 /* [HGM] or restack */
\r
3501 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3502 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3504 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3505 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3506 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3507 x = dragInfo.pos.x - squareSize / 2;
\r
3508 y = dragInfo.pos.y - squareSize / 2;
\r
3509 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3510 ((int) dragged_piece < (int) BlackPawn),
\r
3511 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3514 /* Put the animated piece back into place and draw it */
\r
3515 if (animInfo.piece != EmptySquare) {
\r
3516 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3517 x = boardRect.left + animInfo.pos.x;
\r
3518 y = boardRect.top + animInfo.pos.y;
\r
3519 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3520 ((int) animInfo.piece < (int) BlackPawn),
\r
3521 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3524 /* Release the bufferBitmap by selecting in the old bitmap
\r
3525 * and delete the memory DC
\r
3527 SelectObject(hdcmem, oldBitmap);
\r
3530 /* Set clipping on the target DC */
\r
3531 if (!fullrepaint) {
\r
3532 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3534 GetRgnBox(clips[x], &rect);
\r
3535 DeleteObject(clips[x]);
\r
3536 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3537 rect.right + wpMain.width/2, rect.bottom);
\r
3539 SelectClipRgn(hdc, clips[0]);
\r
3540 for (x = 1; x < num_clips; x++) {
\r
3541 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3542 abort(); // this should never ever happen!
\r
3546 /* Copy the new bitmap onto the screen in one go.
\r
3547 * This way we avoid any flickering
\r
3549 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3550 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3551 boardRect.right - boardRect.left,
\r
3552 boardRect.bottom - boardRect.top,
\r
3553 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3554 if(saveDiagFlag) {
\r
3555 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3556 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3558 GetObject(bufferBitmap, sizeof(b), &b);
\r
3559 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3560 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3561 bih.biWidth = b.bmWidth;
\r
3562 bih.biHeight = b.bmHeight;
\r
3564 bih.biBitCount = b.bmBitsPixel;
\r
3565 bih.biCompression = 0;
\r
3566 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3567 bih.biXPelsPerMeter = 0;
\r
3568 bih.biYPelsPerMeter = 0;
\r
3569 bih.biClrUsed = 0;
\r
3570 bih.biClrImportant = 0;
\r
3571 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3572 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3573 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3574 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3576 wb = b.bmWidthBytes;
\r
3578 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3579 int k = ((int*) pData)[i];
\r
3580 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3581 if(j >= 16) break;
\r
3583 if(j >= nrColors) nrColors = j+1;
\r
3585 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3587 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3588 for(w=0; w<(wb>>2); w+=2) {
\r
3589 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3590 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3591 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3592 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3593 pData[p++] = m | j<<4;
\r
3595 while(p&3) pData[p++] = 0;
\r
3598 wb = ((wb+31)>>5)<<2;
\r
3600 // write BITMAPFILEHEADER
\r
3601 fprintf(diagFile, "BM");
\r
3602 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3603 fputDW(diagFile, 0);
\r
3604 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3605 // write BITMAPINFOHEADER
\r
3606 fputDW(diagFile, 40);
\r
3607 fputDW(diagFile, b.bmWidth);
\r
3608 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3609 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3610 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3611 fputDW(diagFile, 0);
\r
3612 fputDW(diagFile, 0);
\r
3613 fputDW(diagFile, 0);
\r
3614 fputDW(diagFile, 0);
\r
3615 fputDW(diagFile, 0);
\r
3616 fputDW(diagFile, 0);
\r
3617 // write color table
\r
3619 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3620 // write bitmap data
\r
3621 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3622 fputc(pData[i], diagFile);
\r
3626 SelectObject(tmphdc, oldBitmap);
\r
3628 /* Massive cleanup */
\r
3629 for (x = 0; x < num_clips; x++)
\r
3630 DeleteObject(clips[x]);
\r
3633 DeleteObject(bufferBitmap);
\r
3636 ReleaseDC(hwndMain, hdc);
\r
3638 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3640 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3642 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3645 /* CopyBoard(lastDrawn, board);*/
\r
3646 lastDrawnHighlight = highlightInfo;
\r
3647 lastDrawnPremove = premoveHighlightInfo;
\r
3648 lastDrawnFlipView = flipView;
\r
3649 lastDrawnValid[nr] = 1;
\r
3652 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3657 saveDiagFlag = 1; diagFile = f;
\r
3658 HDCDrawPosition(NULL, TRUE, NULL);
\r
3662 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3669 /*---------------------------------------------------------------------------*\
\r
3670 | CLIENT PAINT PROCEDURE
\r
3671 | This is the main event-handler for the WM_PAINT message.
\r
3673 \*---------------------------------------------------------------------------*/
\r
3675 PaintProc(HWND hwnd)
\r
3681 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3682 if (IsIconic(hwnd)) {
\r
3683 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3685 if (!appData.monoMode) {
\r
3686 SelectPalette(hdc, hPal, FALSE);
\r
3687 RealizePalette(hdc);
\r
3689 HDCDrawPosition(hdc, 1, NULL);
\r
3690 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3691 flipView = !flipView; partnerUp = !partnerUp;
\r
3692 HDCDrawPosition(hdc, 1, NULL);
\r
3693 flipView = !flipView; partnerUp = !partnerUp;
\r
3696 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3697 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3698 ETO_CLIPPED|ETO_OPAQUE,
\r
3699 &messageRect, messageText, strlen(messageText), NULL);
\r
3700 SelectObject(hdc, oldFont);
\r
3701 DisplayBothClocks();
\r
3703 EndPaint(hwnd,&ps);
\r
3711 * If the user selects on a border boundary, return -1; if off the board,
\r
3712 * return -2. Otherwise map the event coordinate to the square.
\r
3713 * The offset boardRect.left or boardRect.top must already have been
\r
3714 * subtracted from x.
\r
3716 int EventToSquare(x, limit)
\r
3724 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3726 x /= (squareSize + lineGap);
\r
3738 DropEnable dropEnables[] = {
\r
3739 { 'P', DP_Pawn, "Pawn" },
\r
3740 { 'N', DP_Knight, "Knight" },
\r
3741 { 'B', DP_Bishop, "Bishop" },
\r
3742 { 'R', DP_Rook, "Rook" },
\r
3743 { 'Q', DP_Queen, "Queen" },
\r
3747 SetupDropMenu(HMENU hmenu)
\r
3749 int i, count, enable;
\r
3751 extern char white_holding[], black_holding[];
\r
3752 char item[MSG_SIZ];
\r
3754 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3755 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3756 dropEnables[i].piece);
\r
3758 while (p && *p++ == dropEnables[i].piece) count++;
\r
3759 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3760 enable = count > 0 || !appData.testLegality
\r
3761 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3762 && !appData.icsActive);
\r
3763 ModifyMenu(hmenu, dropEnables[i].command,
\r
3764 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3765 dropEnables[i].command, item);
\r
3769 void DragPieceBegin(int x, int y)
\r
3771 dragInfo.lastpos.x = boardRect.left + x;
\r
3772 dragInfo.lastpos.y = boardRect.top + y;
\r
3773 dragInfo.from.x = fromX;
\r
3774 dragInfo.from.y = fromY;
\r
3775 dragInfo.start = dragInfo.from;
\r
3776 SetCapture(hwndMain);
\r
3779 void DragPieceEnd(int x, int y)
\r
3782 dragInfo.start.x = dragInfo.start.y = -1;
\r
3783 dragInfo.from = dragInfo.start;
\r
3784 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3787 /* Event handler for mouse messages */
\r
3789 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3793 static int recursive = 0;
\r
3795 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3798 if (message == WM_MBUTTONUP) {
\r
3799 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3800 to the middle button: we simulate pressing the left button too!
\r
3802 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3803 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3809 pt.x = LOWORD(lParam);
\r
3810 pt.y = HIWORD(lParam);
\r
3811 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3812 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3813 if (!flipView && y >= 0) {
\r
3814 y = BOARD_HEIGHT - 1 - y;
\r
3816 if (flipView && x >= 0) {
\r
3817 x = BOARD_WIDTH - 1 - x;
\r
3820 switch (message) {
\r
3821 case WM_LBUTTONDOWN:
\r
3822 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3823 if (gameMode == EditPosition) {
\r
3824 SetWhiteToPlayEvent();
\r
3825 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3826 AdjustClock(flipClock, -1);
\r
3827 } else if (gameMode == IcsPlayingBlack ||
\r
3828 gameMode == MachinePlaysWhite) {
\r
3831 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3832 if (gameMode == EditPosition) {
\r
3833 SetBlackToPlayEvent();
\r
3834 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3835 AdjustClock(!flipClock, -1);
\r
3836 } else if (gameMode == IcsPlayingWhite ||
\r
3837 gameMode == MachinePlaysBlack) {
\r
3841 dragInfo.start.x = dragInfo.start.y = -1;
\r
3842 dragInfo.from = dragInfo.start;
\r
3843 if(fromX == -1 && frozen) { // not sure where this is for
\r
3844 fromX = fromY = -1;
\r
3845 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3848 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3849 DrawPosition(TRUE, NULL);
\r
3852 case WM_LBUTTONUP:
\r
3853 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3854 DrawPosition(TRUE, NULL);
\r
3857 case WM_MOUSEMOVE:
\r
3858 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3859 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3860 if ((appData.animateDragging || appData.highlightDragging)
\r
3861 && (wParam & MK_LBUTTON)
\r
3862 && dragInfo.from.x >= 0)
\r
3864 BOOL full_repaint = FALSE;
\r
3866 if (appData.animateDragging) {
\r
3867 dragInfo.pos = pt;
\r
3869 if (appData.highlightDragging) {
\r
3870 SetHighlights(fromX, fromY, x, y);
\r
3871 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3872 full_repaint = TRUE;
\r
3876 DrawPosition( full_repaint, NULL);
\r
3878 dragInfo.lastpos = dragInfo.pos;
\r
3882 case WM_MOUSEWHEEL: // [DM]
\r
3883 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3884 /* Mouse Wheel is being rolled forward
\r
3885 * Play moves forward
\r
3887 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
3888 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3889 /* Mouse Wheel is being rolled backward
\r
3890 * Play moves backward
\r
3892 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
3893 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3897 case WM_MBUTTONUP:
\r
3898 case WM_RBUTTONUP:
\r
3900 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3903 case WM_MBUTTONDOWN:
\r
3904 case WM_RBUTTONDOWN:
\r
3907 fromX = fromY = -1;
\r
3908 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3909 dragInfo.start.x = dragInfo.start.y = -1;
\r
3910 dragInfo.from = dragInfo.start;
\r
3911 dragInfo.lastpos = dragInfo.pos;
\r
3912 if (appData.highlightDragging) {
\r
3913 ClearHighlights();
\r
3916 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3917 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3918 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
3919 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3920 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
3924 DrawPosition(TRUE, NULL);
\r
3926 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3929 if (message == WM_MBUTTONDOWN) {
\r
3930 buttonCount = 3; /* even if system didn't think so */
\r
3931 if (wParam & MK_SHIFT)
\r
3932 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3934 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3935 } else { /* message == WM_RBUTTONDOWN */
\r
3936 /* Just have one menu, on the right button. Windows users don't
\r
3937 think to try the middle one, and sometimes other software steals
\r
3938 it, or it doesn't really exist. */
\r
3939 if(gameInfo.variant != VariantShogi)
\r
3940 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3942 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3946 SetCapture(hwndMain);
3949 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3950 SetupDropMenu(hmenu);
\r
3951 MenuPopup(hwnd, pt, hmenu, -1);
\r
3961 /* Preprocess messages for buttons in main window */
\r
3963 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3965 int id = GetWindowLong(hwnd, GWL_ID);
\r
3968 for (i=0; i<N_BUTTONS; i++) {
\r
3969 if (buttonDesc[i].id == id) break;
\r
3971 if (i == N_BUTTONS) return 0;
\r
3972 switch (message) {
\r
3977 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3978 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3985 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3988 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3989 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3990 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3991 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3993 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3995 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3996 PopUpMoveDialog((char)wParam);
\r
4002 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4005 /* Process messages for Promotion dialog box */
\r
4007 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4011 switch (message) {
\r
4012 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4013 /* Center the dialog over the application window */
\r
4014 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4015 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4016 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4017 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4018 SW_SHOW : SW_HIDE);
\r
4019 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4020 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4021 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4022 PieceToChar(WhiteAngel) != '~') ||
\r
4023 (PieceToChar(BlackAngel) >= 'A' &&
\r
4024 PieceToChar(BlackAngel) != '~') ) ?
\r
4025 SW_SHOW : SW_HIDE);
\r
4026 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4027 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4028 PieceToChar(WhiteMarshall) != '~') ||
\r
4029 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4030 PieceToChar(BlackMarshall) != '~') ) ?
\r
4031 SW_SHOW : SW_HIDE);
\r
4032 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4033 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4034 gameInfo.variant != VariantShogi ?
\r
4035 SW_SHOW : SW_HIDE);
\r
4036 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4037 gameInfo.variant != VariantShogi ?
\r
4038 SW_SHOW : SW_HIDE);
\r
4039 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4040 gameInfo.variant == VariantShogi ?
\r
4041 SW_SHOW : SW_HIDE);
\r
4042 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4043 gameInfo.variant == VariantShogi ?
\r
4044 SW_SHOW : SW_HIDE);
\r
4045 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4046 gameInfo.variant == VariantSuper ?
\r
4047 SW_SHOW : SW_HIDE);
\r
4050 case WM_COMMAND: /* message: received a command */
\r
4051 switch (LOWORD(wParam)) {
\r
4053 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4054 ClearHighlights();
\r
4055 DrawPosition(FALSE, NULL);
\r
4058 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4061 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4064 promoChar = PieceToChar(BlackRook);
\r
4067 promoChar = PieceToChar(BlackBishop);
\r
4069 case PB_Chancellor:
\r
4070 promoChar = PieceToChar(BlackMarshall);
\r
4072 case PB_Archbishop:
\r
4073 promoChar = PieceToChar(BlackAngel);
\r
4076 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4081 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4082 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4083 only show the popup when we are already sure the move is valid or
\r
4084 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4085 will figure out it is a promotion from the promoChar. */
\r
4086 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4087 fromX = fromY = -1;
\r
4088 if (!appData.highlightLastMove) {
\r
4089 ClearHighlights();
\r
4090 DrawPosition(FALSE, NULL);
\r
4097 /* Pop up promotion dialog */
\r
4099 PromotionPopup(HWND hwnd)
\r
4103 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4104 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4105 hwnd, (DLGPROC)lpProc);
\r
4106 FreeProcInstance(lpProc);
\r
4112 DrawPosition(TRUE, NULL);
\r
4113 PromotionPopup(hwndMain);
\r
4116 /* Toggle ShowThinking */
\r
4118 ToggleShowThinking()
\r
4120 appData.showThinking = !appData.showThinking;
\r
4121 ShowThinkingEvent();
\r
4125 LoadGameDialog(HWND hwnd, char* title)
\r
4129 char fileTitle[MSG_SIZ];
\r
4130 f = OpenFileDialog(hwnd, "rb", "",
\r
4131 appData.oldSaveStyle ? "gam" : "pgn",
\r
4133 title, &number, fileTitle, NULL);
\r
4135 cmailMsgLoaded = FALSE;
\r
4136 if (number == 0) {
\r
4137 int error = GameListBuild(f);
\r
4139 DisplayError("Cannot build game list", error);
\r
4140 } else if (!ListEmpty(&gameList) &&
\r
4141 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4142 GameListPopUp(f, fileTitle);
\r
4145 GameListDestroy();
\r
4148 LoadGame(f, number, fileTitle, FALSE);
\r
4152 int get_term_width()
\r
4157 HFONT hfont, hold_font;
\r
4162 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4166 // get the text metrics
\r
4167 hdc = GetDC(hText);
\r
4168 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4169 if (consoleCF.dwEffects & CFE_BOLD)
\r
4170 lf.lfWeight = FW_BOLD;
\r
4171 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4172 lf.lfItalic = TRUE;
\r
4173 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4174 lf.lfStrikeOut = TRUE;
\r
4175 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4176 lf.lfUnderline = TRUE;
\r
4177 hfont = CreateFontIndirect(&lf);
\r
4178 hold_font = SelectObject(hdc, hfont);
\r
4179 GetTextMetrics(hdc, &tm);
\r
4180 SelectObject(hdc, hold_font);
\r
4181 DeleteObject(hfont);
\r
4182 ReleaseDC(hText, hdc);
\r
4184 // get the rectangle
\r
4185 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4187 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4190 void UpdateICSWidth(HWND hText)
\r
4192 LONG old_width, new_width;
\r
4194 new_width = get_term_width(hText, FALSE);
\r
4195 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4196 if (new_width != old_width)
\r
4198 ics_update_width(new_width);
\r
4199 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4204 ChangedConsoleFont()
\r
4207 CHARRANGE tmpsel, sel;
\r
4208 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4209 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4210 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4213 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4214 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4215 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4216 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4217 * size. This was undocumented in the version of MSVC++ that I had
\r
4218 * when I wrote the code, but is apparently documented now.
\r
4220 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4221 cfmt.bCharSet = f->lf.lfCharSet;
\r
4222 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4223 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4224 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4225 /* Why are the following seemingly needed too? */
\r
4226 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4227 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4228 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4230 tmpsel.cpMax = -1; /*999999?*/
\r
4231 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4232 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4233 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4234 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4236 paraf.cbSize = sizeof(paraf);
\r
4237 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4238 paraf.dxStartIndent = 0;
\r
4239 paraf.dxOffset = WRAP_INDENT;
\r
4240 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4241 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4242 UpdateICSWidth(hText);
\r
4245 /*---------------------------------------------------------------------------*\
\r
4247 * Window Proc for main window
\r
4249 \*---------------------------------------------------------------------------*/
\r
4251 /* Process messages for main window, etc. */
\r
4253 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4256 int wmId, wmEvent;
\r
4260 char fileTitle[MSG_SIZ];
\r
4261 char buf[MSG_SIZ];
\r
4262 static SnapData sd;
\r
4264 switch (message) {
\r
4266 case WM_PAINT: /* message: repaint portion of window */
\r
4270 case WM_ERASEBKGND:
\r
4271 if (IsIconic(hwnd)) {
\r
4272 /* Cheat; change the message */
\r
4273 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4275 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4279 case WM_LBUTTONDOWN:
\r
4280 case WM_MBUTTONDOWN:
\r
4281 case WM_RBUTTONDOWN:
\r
4282 case WM_LBUTTONUP:
\r
4283 case WM_MBUTTONUP:
\r
4284 case WM_RBUTTONUP:
\r
4285 case WM_MOUSEMOVE:
\r
4286 case WM_MOUSEWHEEL:
\r
4287 MouseEvent(hwnd, message, wParam, lParam);
\r
4290 JAWS_KB_NAVIGATION
\r
4294 JAWS_ALT_INTERCEPT
\r
4296 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4297 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4298 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4299 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4301 SendMessage(h, message, wParam, lParam);
\r
4302 } else if(lParam != KF_REPEAT) {
\r
4303 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4304 PopUpMoveDialog((char)wParam);
\r
4305 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4306 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4311 case WM_PALETTECHANGED:
\r
4312 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4314 HDC hdc = GetDC(hwndMain);
\r
4315 SelectPalette(hdc, hPal, TRUE);
\r
4316 nnew = RealizePalette(hdc);
\r
4318 paletteChanged = TRUE;
\r
4319 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4321 ReleaseDC(hwnd, hdc);
\r
4325 case WM_QUERYNEWPALETTE:
\r
4326 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4328 HDC hdc = GetDC(hwndMain);
\r
4329 paletteChanged = FALSE;
\r
4330 SelectPalette(hdc, hPal, FALSE);
\r
4331 nnew = RealizePalette(hdc);
\r
4333 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4335 ReleaseDC(hwnd, hdc);
\r
4340 case WM_COMMAND: /* message: command from application menu */
\r
4341 wmId = LOWORD(wParam);
\r
4342 wmEvent = HIWORD(wParam);
\r
4347 SAY("new game enter a move to play against the computer with white");
\r
4350 case IDM_NewGameFRC:
\r
4351 if( NewGameFRC() == 0 ) {
\r
4356 case IDM_NewVariant:
\r
4357 NewVariantPopup(hwnd);
\r
4360 case IDM_LoadGame:
\r
4361 LoadGameDialog(hwnd, "Load Game from File");
\r
4364 case IDM_LoadNextGame:
\r
4368 case IDM_LoadPrevGame:
\r
4372 case IDM_ReloadGame:
\r
4376 case IDM_LoadPosition:
\r
4377 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4378 Reset(FALSE, TRUE);
\r
4381 f = OpenFileDialog(hwnd, "rb", "",
\r
4382 appData.oldSaveStyle ? "pos" : "fen",
\r
4384 "Load Position from File", &number, fileTitle, NULL);
\r
4386 LoadPosition(f, number, fileTitle);
\r
4390 case IDM_LoadNextPosition:
\r
4391 ReloadPosition(1);
\r
4394 case IDM_LoadPrevPosition:
\r
4395 ReloadPosition(-1);
\r
4398 case IDM_ReloadPosition:
\r
4399 ReloadPosition(0);
\r
4402 case IDM_SaveGame:
\r
4403 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4404 f = OpenFileDialog(hwnd, "a", defName,
\r
4405 appData.oldSaveStyle ? "gam" : "pgn",
\r
4407 "Save Game to File", NULL, fileTitle, NULL);
\r
4409 SaveGame(f, 0, "");
\r
4413 case IDM_SavePosition:
\r
4414 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4415 f = OpenFileDialog(hwnd, "a", defName,
\r
4416 appData.oldSaveStyle ? "pos" : "fen",
\r
4418 "Save Position to File", NULL, fileTitle, NULL);
\r
4420 SavePosition(f, 0, "");
\r
4424 case IDM_SaveDiagram:
\r
4425 defName = "diagram";
\r
4426 f = OpenFileDialog(hwnd, "wb", defName,
\r
4429 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4435 case IDM_CopyGame:
\r
4436 CopyGameToClipboard();
\r
4439 case IDM_PasteGame:
\r
4440 PasteGameFromClipboard();
\r
4443 case IDM_CopyGameListToClipboard:
\r
4444 CopyGameListToClipboard();
\r
4447 /* [AS] Autodetect FEN or PGN data */
\r
4448 case IDM_PasteAny:
\r
4449 PasteGameOrFENFromClipboard();
\r
4452 /* [AS] Move history */
\r
4453 case IDM_ShowMoveHistory:
\r
4454 if( MoveHistoryIsUp() ) {
\r
4455 MoveHistoryPopDown();
\r
4458 MoveHistoryPopUp();
\r
4462 /* [AS] Eval graph */
\r
4463 case IDM_ShowEvalGraph:
\r
4464 if( EvalGraphIsUp() ) {
\r
4465 EvalGraphPopDown();
\r
4469 SetFocus(hwndMain);
\r
4473 /* [AS] Engine output */
\r
4474 case IDM_ShowEngineOutput:
\r
4475 if( EngineOutputIsUp() ) {
\r
4476 EngineOutputPopDown();
\r
4479 EngineOutputPopUp();
\r
4483 /* [AS] User adjudication */
\r
4484 case IDM_UserAdjudication_White:
\r
4485 UserAdjudicationEvent( +1 );
\r
4488 case IDM_UserAdjudication_Black:
\r
4489 UserAdjudicationEvent( -1 );
\r
4492 case IDM_UserAdjudication_Draw:
\r
4493 UserAdjudicationEvent( 0 );
\r
4496 /* [AS] Game list options dialog */
\r
4497 case IDM_GameListOptions:
\r
4498 GameListOptions();
\r
4505 case IDM_CopyPosition:
\r
4506 CopyFENToClipboard();
\r
4509 case IDM_PastePosition:
\r
4510 PasteFENFromClipboard();
\r
4513 case IDM_MailMove:
\r
4517 case IDM_ReloadCMailMsg:
\r
4518 Reset(TRUE, TRUE);
\r
4519 ReloadCmailMsgEvent(FALSE);
\r
4522 case IDM_Minimize:
\r
4523 ShowWindow(hwnd, SW_MINIMIZE);
\r
4530 case IDM_MachineWhite:
\r
4531 MachineWhiteEvent();
\r
4533 * refresh the tags dialog only if it's visible
\r
4535 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4537 tags = PGNTags(&gameInfo);
\r
4538 TagsPopUp(tags, CmailMsg());
\r
4541 SAY("computer starts playing white");
\r
4544 case IDM_MachineBlack:
\r
4545 MachineBlackEvent();
\r
4547 * refresh the tags dialog only if it's visible
\r
4549 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4551 tags = PGNTags(&gameInfo);
\r
4552 TagsPopUp(tags, CmailMsg());
\r
4555 SAY("computer starts playing black");
\r
4558 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4559 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4560 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4561 appData.matchGames = appData.defaultMatchGames;
\r
4564 case IDM_TwoMachines:
\r
4565 TwoMachinesEvent();
\r
4567 * refresh the tags dialog only if it's visible
\r
4569 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4571 tags = PGNTags(&gameInfo);
\r
4572 TagsPopUp(tags, CmailMsg());
\r
4575 SAY("computer starts playing both sides");
\r
4578 case IDM_AnalysisMode:
\r
4579 if (!first.analysisSupport) {
\r
4580 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4581 DisplayError(buf, 0);
\r
4583 SAY("analyzing current position");
\r
4584 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4585 if (appData.icsActive) {
\r
4586 if (gameMode != IcsObserving) {
\r
4587 sprintf(buf, "You are not observing a game");
\r
4588 DisplayError(buf, 0);
\r
4589 /* secure check */
\r
4590 if (appData.icsEngineAnalyze) {
\r
4591 if (appData.debugMode)
\r
4592 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4593 ExitAnalyzeMode();
\r
4599 /* if enable, user want disable icsEngineAnalyze */
\r
4600 if (appData.icsEngineAnalyze) {
\r
4601 ExitAnalyzeMode();
\r
4605 appData.icsEngineAnalyze = TRUE;
\r
4606 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4609 if (!appData.showThinking) ToggleShowThinking();
\r
4610 AnalyzeModeEvent();
\r
4614 case IDM_AnalyzeFile:
\r
4615 if (!first.analysisSupport) {
\r
4616 char buf[MSG_SIZ];
\r
4617 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4618 DisplayError(buf, 0);
\r
4620 if (!appData.showThinking) ToggleShowThinking();
\r
4621 AnalyzeFileEvent();
\r
4622 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4623 AnalysisPeriodicEvent(1);
\r
4627 case IDM_IcsClient:
\r
4631 case IDM_EditGame:
\r
4636 case IDM_EditPosition:
\r
4637 EditPositionEvent();
\r
4638 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4641 case IDM_Training:
\r
4645 case IDM_ShowGameList:
\r
4646 ShowGameListProc();
\r
4649 case IDM_EditTags:
\r
4653 case IDM_EditComment:
\r
4654 if (commentUp && editComment) {
\r
4657 EditCommentEvent();
\r
4677 case IDM_CallFlag:
\r
4697 case IDM_StopObserving:
\r
4698 StopObservingEvent();
\r
4701 case IDM_StopExamining:
\r
4702 StopExaminingEvent();
\r
4706 UploadGameEvent();
\r
4709 case IDM_TypeInMove:
\r
4710 PopUpMoveDialog('\000');
\r
4713 case IDM_TypeInName:
\r
4714 PopUpNameDialog('\000');
\r
4717 case IDM_Backward:
\r
4719 SetFocus(hwndMain);
\r
4726 SetFocus(hwndMain);
\r
4731 SetFocus(hwndMain);
\r
4736 SetFocus(hwndMain);
\r
4740 RevertEvent(FALSE);
\r
4743 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4744 RevertEvent(TRUE);
\r
4747 case IDM_TruncateGame:
\r
4748 TruncateGameEvent();
\r
4755 case IDM_RetractMove:
\r
4756 RetractMoveEvent();
\r
4759 case IDM_FlipView:
\r
4760 flipView = !flipView;
\r
4761 DrawPosition(FALSE, NULL);
\r
4764 case IDM_FlipClock:
\r
4765 flipClock = !flipClock;
\r
4766 DisplayBothClocks();
\r
4767 DrawPosition(FALSE, NULL);
\r
4770 case IDM_MuteSounds:
\r
4771 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4772 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4773 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4776 case IDM_GeneralOptions:
\r
4777 GeneralOptionsPopup(hwnd);
\r
4778 DrawPosition(TRUE, NULL);
\r
4781 case IDM_BoardOptions:
\r
4782 BoardOptionsPopup(hwnd);
\r
4785 case IDM_EnginePlayOptions:
\r
4786 EnginePlayOptionsPopup(hwnd);
\r
4789 case IDM_Engine1Options:
\r
4790 EngineOptionsPopup(hwnd, &first);
\r
4793 case IDM_Engine2Options:
\r
4794 EngineOptionsPopup(hwnd, &second);
\r
4797 case IDM_OptionsUCI:
\r
4798 UciOptionsPopup(hwnd);
\r
4801 case IDM_IcsOptions:
\r
4802 IcsOptionsPopup(hwnd);
\r
4806 FontsOptionsPopup(hwnd);
\r
4810 SoundOptionsPopup(hwnd);
\r
4813 case IDM_CommPort:
\r
4814 CommPortOptionsPopup(hwnd);
\r
4817 case IDM_LoadOptions:
\r
4818 LoadOptionsPopup(hwnd);
\r
4821 case IDM_SaveOptions:
\r
4822 SaveOptionsPopup(hwnd);
\r
4825 case IDM_TimeControl:
\r
4826 TimeControlOptionsPopup(hwnd);
\r
4829 case IDM_SaveSettings:
\r
4830 SaveSettings(settingsFileName);
\r
4833 case IDM_SaveSettingsOnExit:
\r
4834 saveSettingsOnExit = !saveSettingsOnExit;
\r
4835 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4836 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4837 MF_CHECKED : MF_UNCHECKED));
\r
4848 case IDM_AboutGame:
\r
4853 appData.debugMode = !appData.debugMode;
\r
4854 if (appData.debugMode) {
\r
4855 char dir[MSG_SIZ];
\r
4856 GetCurrentDirectory(MSG_SIZ, dir);
\r
4857 SetCurrentDirectory(installDir);
\r
4858 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4859 SetCurrentDirectory(dir);
\r
4860 setbuf(debugFP, NULL);
\r
4867 case IDM_HELPCONTENTS:
\r
4868 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4869 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4870 MessageBox (GetFocus(),
\r
4871 "Unable to activate help",
\r
4872 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4876 case IDM_HELPSEARCH:
\r
4877 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4878 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4879 MessageBox (GetFocus(),
\r
4880 "Unable to activate help",
\r
4881 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4885 case IDM_HELPHELP:
\r
4886 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4887 MessageBox (GetFocus(),
\r
4888 "Unable to activate help",
\r
4889 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4894 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4896 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4897 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4898 FreeProcInstance(lpProc);
\r
4901 case IDM_DirectCommand1:
\r
4902 AskQuestionEvent("Direct Command",
\r
4903 "Send to chess program:", "", "1");
\r
4905 case IDM_DirectCommand2:
\r
4906 AskQuestionEvent("Direct Command",
\r
4907 "Send to second chess program:", "", "2");
\r
4910 case EP_WhitePawn:
\r
4911 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4912 fromX = fromY = -1;
\r
4915 case EP_WhiteKnight:
\r
4916 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4917 fromX = fromY = -1;
\r
4920 case EP_WhiteBishop:
\r
4921 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4922 fromX = fromY = -1;
\r
4925 case EP_WhiteRook:
\r
4926 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4927 fromX = fromY = -1;
\r
4930 case EP_WhiteQueen:
\r
4931 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4932 fromX = fromY = -1;
\r
4935 case EP_WhiteFerz:
\r
4936 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4937 fromX = fromY = -1;
\r
4940 case EP_WhiteWazir:
\r
4941 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4942 fromX = fromY = -1;
\r
4945 case EP_WhiteAlfil:
\r
4946 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4947 fromX = fromY = -1;
\r
4950 case EP_WhiteCannon:
\r
4951 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4952 fromX = fromY = -1;
\r
4955 case EP_WhiteCardinal:
\r
4956 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4957 fromX = fromY = -1;
\r
4960 case EP_WhiteMarshall:
\r
4961 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4962 fromX = fromY = -1;
\r
4965 case EP_WhiteKing:
\r
4966 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4967 fromX = fromY = -1;
\r
4970 case EP_BlackPawn:
\r
4971 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4972 fromX = fromY = -1;
\r
4975 case EP_BlackKnight:
\r
4976 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4977 fromX = fromY = -1;
\r
4980 case EP_BlackBishop:
\r
4981 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4982 fromX = fromY = -1;
\r
4985 case EP_BlackRook:
\r
4986 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4987 fromX = fromY = -1;
\r
4990 case EP_BlackQueen:
\r
4991 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4992 fromX = fromY = -1;
\r
4995 case EP_BlackFerz:
\r
4996 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4997 fromX = fromY = -1;
\r
5000 case EP_BlackWazir:
\r
5001 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5002 fromX = fromY = -1;
\r
5005 case EP_BlackAlfil:
\r
5006 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5007 fromX = fromY = -1;
\r
5010 case EP_BlackCannon:
\r
5011 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5012 fromX = fromY = -1;
\r
5015 case EP_BlackCardinal:
\r
5016 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5017 fromX = fromY = -1;
\r
5020 case EP_BlackMarshall:
\r
5021 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5022 fromX = fromY = -1;
\r
5025 case EP_BlackKing:
\r
5026 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5027 fromX = fromY = -1;
\r
5030 case EP_EmptySquare:
\r
5031 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5032 fromX = fromY = -1;
\r
5035 case EP_ClearBoard:
\r
5036 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5037 fromX = fromY = -1;
\r
5041 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5042 fromX = fromY = -1;
\r
5046 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5047 fromX = fromY = -1;
\r
5051 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5052 fromX = fromY = -1;
\r
5056 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5057 fromX = fromY = -1;
\r
5061 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5062 fromX = fromY = -1;
\r
5066 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5067 fromX = fromY = -1;
\r
5071 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5072 fromX = fromY = -1;
\r
5076 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5077 fromX = fromY = -1;
\r
5081 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5082 fromX = fromY = -1;
\r
5086 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5092 case CLOCK_TIMER_ID:
\r
5093 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5094 clockTimerEvent = 0;
\r
5095 DecrementClocks(); /* call into back end */
\r
5097 case LOAD_GAME_TIMER_ID:
\r
5098 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5099 loadGameTimerEvent = 0;
\r
5100 AutoPlayGameLoop(); /* call into back end */
\r
5102 case ANALYSIS_TIMER_ID:
\r
5103 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5104 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5105 AnalysisPeriodicEvent(0);
\r
5107 KillTimer(hwnd, analysisTimerEvent);
\r
5108 analysisTimerEvent = 0;
\r
5111 case DELAYED_TIMER_ID:
\r
5112 KillTimer(hwnd, delayedTimerEvent);
\r
5113 delayedTimerEvent = 0;
\r
5114 delayedTimerCallback();
\r
5119 case WM_USER_Input:
\r
5120 InputEvent(hwnd, message, wParam, lParam);
\r
5123 /* [AS] Also move "attached" child windows */
\r
5124 case WM_WINDOWPOSCHANGING:
\r
5126 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5127 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5129 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5130 /* Window is moving */
\r
5133 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5134 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5135 rcMain.right = wpMain.x + wpMain.width;
\r
5136 rcMain.top = wpMain.y;
\r
5137 rcMain.bottom = wpMain.y + wpMain.height;
\r
5139 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5140 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5141 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5142 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5143 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5144 wpMain.x = lpwp->x;
\r
5145 wpMain.y = lpwp->y;
\r
5150 /* [AS] Snapping */
\r
5151 case WM_ENTERSIZEMOVE:
\r
5152 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5153 if (hwnd == hwndMain) {
\r
5154 doingSizing = TRUE;
\r
5157 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5161 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5162 if (hwnd == hwndMain) {
\r
5163 lastSizing = wParam;
\r
5168 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5169 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5171 case WM_EXITSIZEMOVE:
\r
5172 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5173 if (hwnd == hwndMain) {
\r
5175 doingSizing = FALSE;
\r
5176 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5177 GetClientRect(hwnd, &client);
\r
5178 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5180 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5182 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5185 case WM_DESTROY: /* message: window being destroyed */
\r
5186 PostQuitMessage(0);
\r
5190 if (hwnd == hwndMain) {
\r
5195 default: /* Passes it on if unprocessed */
\r
5196 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5201 /*---------------------------------------------------------------------------*\
\r
5203 * Misc utility routines
\r
5205 \*---------------------------------------------------------------------------*/
\r
5208 * Decent random number generator, at least not as bad as Windows
\r
5209 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5211 unsigned int randstate;
\r
5216 randstate = randstate * 1664525 + 1013904223;
\r
5217 return (int) randstate & 0x7fffffff;
\r
5221 mysrandom(unsigned int seed)
\r
5228 * returns TRUE if user selects a different color, FALSE otherwise
\r
5232 ChangeColor(HWND hwnd, COLORREF *which)
\r
5234 static BOOL firstTime = TRUE;
\r
5235 static DWORD customColors[16];
\r
5237 COLORREF newcolor;
\r
5242 /* Make initial colors in use available as custom colors */
\r
5243 /* Should we put the compiled-in defaults here instead? */
\r
5245 customColors[i++] = lightSquareColor & 0xffffff;
\r
5246 customColors[i++] = darkSquareColor & 0xffffff;
\r
5247 customColors[i++] = whitePieceColor & 0xffffff;
\r
5248 customColors[i++] = blackPieceColor & 0xffffff;
\r
5249 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5250 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5252 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5253 customColors[i++] = textAttribs[ccl].color;
\r
5255 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5256 firstTime = FALSE;
\r
5259 cc.lStructSize = sizeof(cc);
\r
5260 cc.hwndOwner = hwnd;
\r
5261 cc.hInstance = NULL;
\r
5262 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5263 cc.lpCustColors = (LPDWORD) customColors;
\r
5264 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5266 if (!ChooseColor(&cc)) return FALSE;
\r
5268 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5269 if (newcolor == *which) return FALSE;
\r
5270 *which = newcolor;
\r
5274 InitDrawingColors();
\r
5275 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5280 MyLoadSound(MySound *ms)
\r
5286 if (ms->data) free(ms->data);
\r
5289 switch (ms->name[0]) {
\r
5295 /* System sound from Control Panel. Don't preload here. */
\r
5299 if (ms->name[1] == NULLCHAR) {
\r
5300 /* "!" alone = silence */
\r
5303 /* Builtin wave resource. Error if not found. */
\r
5304 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5305 if (h == NULL) break;
\r
5306 ms->data = (void *)LoadResource(hInst, h);
\r
5307 if (h == NULL) break;
\r
5312 /* .wav file. Error if not found. */
\r
5313 f = fopen(ms->name, "rb");
\r
5314 if (f == NULL) break;
\r
5315 if (fstat(fileno(f), &st) < 0) break;
\r
5316 ms->data = malloc(st.st_size);
\r
5317 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5323 char buf[MSG_SIZ];
\r
5324 sprintf(buf, "Error loading sound %s", ms->name);
\r
5325 DisplayError(buf, GetLastError());
\r
5331 MyPlaySound(MySound *ms)
\r
5333 BOOLEAN ok = FALSE;
\r
5335 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5336 switch (ms->name[0]) {
\r
5338 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5343 /* System sound from Control Panel (deprecated feature).
\r
5344 "$" alone or an unset sound name gets default beep (still in use). */
\r
5345 if (ms->name[1]) {
\r
5346 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5348 if (!ok) ok = MessageBeep(MB_OK);
\r
5351 /* Builtin wave resource, or "!" alone for silence */
\r
5352 if (ms->name[1]) {
\r
5353 if (ms->data == NULL) return FALSE;
\r
5354 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5360 /* .wav file. Error if not found. */
\r
5361 if (ms->data == NULL) return FALSE;
\r
5362 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5365 /* Don't print an error: this can happen innocently if the sound driver
\r
5366 is busy; for instance, if another instance of WinBoard is playing
\r
5367 a sound at about the same time. */
\r
5373 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5376 OPENFILENAME *ofn;
\r
5377 static UINT *number; /* gross that this is static */
\r
5379 switch (message) {
\r
5380 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5381 /* Center the dialog over the application window */
\r
5382 ofn = (OPENFILENAME *) lParam;
\r
5383 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5384 number = (UINT *) ofn->lCustData;
\r
5385 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5389 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5390 return FALSE; /* Allow for further processing */
\r
5393 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5394 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5396 return FALSE; /* Allow for further processing */
\r
5402 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5404 static UINT *number;
\r
5405 OPENFILENAME *ofname;
\r
5408 case WM_INITDIALOG:
\r
5409 ofname = (OPENFILENAME *)lParam;
\r
5410 number = (UINT *)(ofname->lCustData);
\r
5413 ofnot = (OFNOTIFY *)lParam;
\r
5414 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5415 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5424 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5425 char *nameFilt, char *dlgTitle, UINT *number,
\r
5426 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5428 OPENFILENAME openFileName;
\r
5429 char buf1[MSG_SIZ];
\r
5432 if (fileName == NULL) fileName = buf1;
\r
5433 if (defName == NULL) {
\r
5434 strcpy(fileName, "*.");
\r
5435 strcat(fileName, defExt);
\r
5437 strcpy(fileName, defName);
\r
5439 if (fileTitle) strcpy(fileTitle, "");
\r
5440 if (number) *number = 0;
\r
5442 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5443 openFileName.hwndOwner = hwnd;
\r
5444 openFileName.hInstance = (HANDLE) hInst;
\r
5445 openFileName.lpstrFilter = nameFilt;
\r
5446 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5447 openFileName.nMaxCustFilter = 0L;
\r
5448 openFileName.nFilterIndex = 1L;
\r
5449 openFileName.lpstrFile = fileName;
\r
5450 openFileName.nMaxFile = MSG_SIZ;
\r
5451 openFileName.lpstrFileTitle = fileTitle;
\r
5452 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5453 openFileName.lpstrInitialDir = NULL;
\r
5454 openFileName.lpstrTitle = dlgTitle;
\r
5455 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5456 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5457 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5458 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5459 openFileName.nFileOffset = 0;
\r
5460 openFileName.nFileExtension = 0;
\r
5461 openFileName.lpstrDefExt = defExt;
\r
5462 openFileName.lCustData = (LONG) number;
\r
5463 openFileName.lpfnHook = oldDialog ?
\r
5464 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5465 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5467 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5468 GetOpenFileName(&openFileName)) {
\r
5469 /* open the file */
\r
5470 f = fopen(openFileName.lpstrFile, write);
\r
5472 MessageBox(hwnd, "File open failed", NULL,
\r
5473 MB_OK|MB_ICONEXCLAMATION);
\r
5477 int err = CommDlgExtendedError();
\r
5478 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5487 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5489 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5492 * Get the first pop-up menu in the menu template. This is the
\r
5493 * menu that TrackPopupMenu displays.
\r
5495 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5497 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5500 * TrackPopup uses screen coordinates, so convert the
\r
5501 * coordinates of the mouse click to screen coordinates.
\r
5503 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5505 /* Draw and track the floating pop-up menu. */
\r
5506 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5507 pt.x, pt.y, 0, hwnd, NULL);
\r
5509 /* Destroy the menu.*/
\r
5510 DestroyMenu(hmenu);
\r
5515 int sizeX, sizeY, newSizeX, newSizeY;
\r
5517 } ResizeEditPlusButtonsClosure;
\r
5520 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5522 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5526 if (hChild == cl->hText) return TRUE;
\r
5527 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5528 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5529 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5530 ScreenToClient(cl->hDlg, &pt);
\r
5531 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5532 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5536 /* Resize a dialog that has a (rich) edit field filling most of
\r
5537 the top, with a row of buttons below */
\r
5539 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5542 int newTextHeight, newTextWidth;
\r
5543 ResizeEditPlusButtonsClosure cl;
\r
5545 /*if (IsIconic(hDlg)) return;*/
\r
5546 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5548 cl.hdwp = BeginDeferWindowPos(8);
\r
5550 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5551 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5552 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5553 if (newTextHeight < 0) {
\r
5554 newSizeY += -newTextHeight;
\r
5555 newTextHeight = 0;
\r
5557 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5558 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5564 cl.newSizeX = newSizeX;
\r
5565 cl.newSizeY = newSizeY;
\r
5566 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5568 EndDeferWindowPos(cl.hdwp);
\r
5571 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5573 RECT rChild, rParent;
\r
5574 int wChild, hChild, wParent, hParent;
\r
5575 int wScreen, hScreen, xNew, yNew;
\r
5578 /* Get the Height and Width of the child window */
\r
5579 GetWindowRect (hwndChild, &rChild);
\r
5580 wChild = rChild.right - rChild.left;
\r
5581 hChild = rChild.bottom - rChild.top;
\r
5583 /* Get the Height and Width of the parent window */
\r
5584 GetWindowRect (hwndParent, &rParent);
\r
5585 wParent = rParent.right - rParent.left;
\r
5586 hParent = rParent.bottom - rParent.top;
\r
5588 /* Get the display limits */
\r
5589 hdc = GetDC (hwndChild);
\r
5590 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5591 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5592 ReleaseDC(hwndChild, hdc);
\r
5594 /* Calculate new X position, then adjust for screen */
\r
5595 xNew = rParent.left + ((wParent - wChild) /2);
\r
5598 } else if ((xNew+wChild) > wScreen) {
\r
5599 xNew = wScreen - wChild;
\r
5602 /* Calculate new Y position, then adjust for screen */
\r
5604 yNew = rParent.top + ((hParent - hChild) /2);
\r
5607 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5612 } else if ((yNew+hChild) > hScreen) {
\r
5613 yNew = hScreen - hChild;
\r
5616 /* Set it, and return */
\r
5617 return SetWindowPos (hwndChild, NULL,
\r
5618 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5621 /* Center one window over another */
\r
5622 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5624 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5627 /*---------------------------------------------------------------------------*\
\r
5629 * Startup Dialog functions
\r
5631 \*---------------------------------------------------------------------------*/
\r
5633 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5635 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5637 while (*cd != NULL) {
\r
5638 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5644 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5646 char buf1[MAX_ARG_LEN];
\r
5649 if (str[0] == '@') {
\r
5650 FILE* f = fopen(str + 1, "r");
\r
5652 DisplayFatalError(str + 1, errno, 2);
\r
5655 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5657 buf1[len] = NULLCHAR;
\r
5661 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5664 char buf[MSG_SIZ];
\r
5665 char *end = strchr(str, '\n');
\r
5666 if (end == NULL) return;
\r
5667 memcpy(buf, str, end - str);
\r
5668 buf[end - str] = NULLCHAR;
\r
5669 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5675 SetStartupDialogEnables(HWND hDlg)
\r
5677 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5678 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5679 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5680 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5681 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5682 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5683 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5684 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5685 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5686 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5687 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5688 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5689 IsDlgButtonChecked(hDlg, OPT_View));
\r
5693 QuoteForFilename(char *filename)
\r
5695 int dquote, space;
\r
5696 dquote = strchr(filename, '"') != NULL;
\r
5697 space = strchr(filename, ' ') != NULL;
\r
5698 if (dquote || space) {
\r
5710 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5712 char buf[MSG_SIZ];
\r
5715 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5716 q = QuoteForFilename(nthcp);
\r
5717 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5718 if (*nthdir != NULLCHAR) {
\r
5719 q = QuoteForFilename(nthdir);
\r
5720 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5722 if (*nthcp == NULLCHAR) {
\r
5723 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5724 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5725 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5726 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5731 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5733 char buf[MSG_SIZ];
\r
5737 switch (message) {
\r
5738 case WM_INITDIALOG:
\r
5739 /* Center the dialog */
\r
5740 CenterWindow (hDlg, GetDesktopWindow());
\r
5741 /* Initialize the dialog items */
\r
5742 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5743 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5744 firstChessProgramNames);
\r
5745 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5746 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5747 secondChessProgramNames);
\r
5748 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5749 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5750 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5751 if (*appData.icsHelper != NULLCHAR) {
\r
5752 char *q = QuoteForFilename(appData.icsHelper);
\r
5753 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5755 if (*appData.icsHost == NULLCHAR) {
\r
5756 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5757 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5758 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5759 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5760 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5763 if (appData.icsActive) {
\r
5764 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5766 else if (appData.noChessProgram) {
\r
5767 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5770 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5773 SetStartupDialogEnables(hDlg);
\r
5777 switch (LOWORD(wParam)) {
\r
5779 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5780 strcpy(buf, "/fcp=");
\r
5781 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5783 ParseArgs(StringGet, &p);
\r
5784 strcpy(buf, "/scp=");
\r
5785 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5787 ParseArgs(StringGet, &p);
\r
5788 appData.noChessProgram = FALSE;
\r
5789 appData.icsActive = FALSE;
\r
5790 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5791 strcpy(buf, "/ics /icshost=");
\r
5792 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5794 ParseArgs(StringGet, &p);
\r
5795 if (appData.zippyPlay) {
\r
5796 strcpy(buf, "/fcp=");
\r
5797 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5799 ParseArgs(StringGet, &p);
\r
5801 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5802 appData.noChessProgram = TRUE;
\r
5803 appData.icsActive = FALSE;
\r
5805 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5806 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5809 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5810 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5812 ParseArgs(StringGet, &p);
\r
5814 EndDialog(hDlg, TRUE);
\r
5821 case IDM_HELPCONTENTS:
\r
5822 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5823 MessageBox (GetFocus(),
\r
5824 "Unable to activate help",
\r
5825 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5830 SetStartupDialogEnables(hDlg);
\r
5838 /*---------------------------------------------------------------------------*\
\r
5840 * About box dialog functions
\r
5842 \*---------------------------------------------------------------------------*/
\r
5844 /* Process messages for "About" dialog box */
\r
5846 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5848 switch (message) {
\r
5849 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5850 /* Center the dialog over the application window */
\r
5851 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5852 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5856 case WM_COMMAND: /* message: received a command */
\r
5857 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5858 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5859 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5867 /*---------------------------------------------------------------------------*\
\r
5869 * Comment Dialog functions
\r
5871 \*---------------------------------------------------------------------------*/
\r
5874 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5876 static HANDLE hwndText = NULL;
\r
5877 int len, newSizeX, newSizeY, flags;
\r
5878 static int sizeX, sizeY;
\r
5883 switch (message) {
\r
5884 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5885 /* Initialize the dialog items */
\r
5886 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5887 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5888 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5889 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5890 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5891 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5892 SetWindowText(hDlg, commentTitle);
\r
5893 if (editComment) {
\r
5894 SetFocus(hwndText);
\r
5896 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5898 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5899 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5900 MAKELPARAM(FALSE, 0));
\r
5901 /* Size and position the dialog */
\r
5902 if (!commentDialog) {
\r
5903 commentDialog = hDlg;
\r
5904 flags = SWP_NOZORDER;
\r
5905 GetClientRect(hDlg, &rect);
\r
5906 sizeX = rect.right;
\r
5907 sizeY = rect.bottom;
\r
5908 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5909 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5910 WINDOWPLACEMENT wp;
\r
5911 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5912 wp.length = sizeof(WINDOWPLACEMENT);
\r
5914 wp.showCmd = SW_SHOW;
\r
5915 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5916 wp.rcNormalPosition.left = wpComment.x;
\r
5917 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5918 wp.rcNormalPosition.top = wpComment.y;
\r
5919 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5920 SetWindowPlacement(hDlg, &wp);
\r
5922 GetClientRect(hDlg, &rect);
\r
5923 newSizeX = rect.right;
\r
5924 newSizeY = rect.bottom;
\r
5925 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5926 newSizeX, newSizeY);
\r
5931 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5934 case WM_COMMAND: /* message: received a command */
\r
5935 switch (LOWORD(wParam)) {
\r
5937 if (editComment) {
\r
5939 /* Read changed options from the dialog box */
\r
5940 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5941 len = GetWindowTextLength(hwndText);
\r
5942 str = (char *) malloc(len + 1);
\r
5943 GetWindowText(hwndText, str, len + 1);
\r
5952 ReplaceComment(commentIndex, str);
\r
5959 case OPT_CancelComment:
\r
5963 case OPT_ClearComment:
\r
5964 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5967 case OPT_EditComment:
\r
5968 EditCommentEvent();
\r
5976 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5977 if( wParam == OPT_CommentText ) {
\r
5978 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5980 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5984 pt.x = LOWORD( lpMF->lParam );
\r
5985 pt.y = HIWORD( lpMF->lParam );
\r
5987 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5989 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5990 len = GetWindowTextLength(hwndText);
\r
5991 str = (char *) malloc(len + 1);
\r
5992 GetWindowText(hwndText, str, len + 1);
\r
5993 ReplaceComment(commentIndex, str);
\r
5994 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5995 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5998 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5999 lpMF->msg = WM_USER;
\r
6007 newSizeX = LOWORD(lParam);
\r
6008 newSizeY = HIWORD(lParam);
\r
6009 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6014 case WM_GETMINMAXINFO:
\r
6015 /* Prevent resizing window too small */
\r
6016 mmi = (MINMAXINFO *) lParam;
\r
6017 mmi->ptMinTrackSize.x = 100;
\r
6018 mmi->ptMinTrackSize.y = 100;
\r
6025 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6030 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6032 if (str == NULL) str = "";
\r
6033 p = (char *) malloc(2 * strlen(str) + 2);
\r
6036 if (*str == '\n') *q++ = '\r';
\r
6040 if (commentText != NULL) free(commentText);
\r
6042 commentIndex = index;
\r
6043 commentTitle = title;
\r
6045 editComment = edit;
\r
6047 if (commentDialog) {
\r
6048 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6049 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6051 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6052 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6053 hwndMain, (DLGPROC)lpProc);
\r
6054 FreeProcInstance(lpProc);
\r
6060 /*---------------------------------------------------------------------------*\
\r
6062 * Type-in move dialog functions
\r
6064 \*---------------------------------------------------------------------------*/
\r
6067 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6069 char move[MSG_SIZ];
\r
6071 ChessMove moveType;
\r
6072 int fromX, fromY, toX, toY;
\r
6075 switch (message) {
\r
6076 case WM_INITDIALOG:
\r
6077 move[0] = (char) lParam;
\r
6078 move[1] = NULLCHAR;
\r
6079 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6080 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6081 SetWindowText(hInput, move);
\r
6083 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6087 switch (LOWORD(wParam)) {
\r
6089 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6090 { int n; Board board;
\r
6092 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6093 EditPositionPasteFEN(move);
\r
6094 EndDialog(hDlg, TRUE);
\r
6097 // [HGM] movenum: allow move number to be typed in any mode
\r
6098 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6100 EndDialog(hDlg, TRUE);
\r
6104 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6105 gameMode != Training) {
\r
6106 DisplayMoveError("Displayed move is not current");
\r
6108 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6109 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6110 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6111 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6112 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6113 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6114 if (gameMode != Training)
\r
6115 forwardMostMove = currentMove;
\r
6116 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6118 DisplayMoveError("Could not parse move");
\r
6121 EndDialog(hDlg, TRUE);
\r
6124 EndDialog(hDlg, FALSE);
\r
6135 PopUpMoveDialog(char firstchar)
\r
6139 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6140 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6141 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6142 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6143 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6144 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6145 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6146 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6147 gameMode == Training) {
\r
6148 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6149 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6150 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6151 FreeProcInstance(lpProc);
\r
6155 /*---------------------------------------------------------------------------*\
\r
6157 * Type-in name dialog functions
\r
6159 \*---------------------------------------------------------------------------*/
\r
6162 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6164 char move[MSG_SIZ];
\r
6167 switch (message) {
\r
6168 case WM_INITDIALOG:
\r
6169 move[0] = (char) lParam;
\r
6170 move[1] = NULLCHAR;
\r
6171 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6172 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6173 SetWindowText(hInput, move);
\r
6175 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6179 switch (LOWORD(wParam)) {
\r
6181 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6182 appData.userName = strdup(move);
\r
6185 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6186 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6187 DisplayTitle(move);
\r
6191 EndDialog(hDlg, TRUE);
\r
6194 EndDialog(hDlg, FALSE);
\r
6205 PopUpNameDialog(char firstchar)
\r
6209 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6210 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6211 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6212 FreeProcInstance(lpProc);
\r
6215 /*---------------------------------------------------------------------------*\
\r
6219 \*---------------------------------------------------------------------------*/
\r
6221 /* Nonmodal error box */
\r
6222 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6223 WPARAM wParam, LPARAM lParam);
\r
6226 ErrorPopUp(char *title, char *content)
\r
6230 BOOLEAN modal = hwndMain == NULL;
\r
6248 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6249 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6252 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6254 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6255 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6256 hwndMain, (DLGPROC)lpProc);
\r
6257 FreeProcInstance(lpProc);
\r
6264 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6265 if (errorDialog == NULL) return;
\r
6266 DestroyWindow(errorDialog);
\r
6267 errorDialog = NULL;
\r
6268 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6272 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6277 switch (message) {
\r
6278 case WM_INITDIALOG:
\r
6279 GetWindowRect(hDlg, &rChild);
\r
6282 SetWindowPos(hDlg, NULL, rChild.left,
\r
6283 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6284 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6288 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6289 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6290 and it doesn't work when you resize the dialog.
\r
6291 For now, just give it a default position.
\r
6293 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6295 errorDialog = hDlg;
\r
6296 SetWindowText(hDlg, errorTitle);
\r
6297 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6298 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6302 switch (LOWORD(wParam)) {
\r
6305 if (errorDialog == hDlg) errorDialog = NULL;
\r
6306 DestroyWindow(hDlg);
\r
6318 HWND gothicDialog = NULL;
\r
6321 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6325 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6327 switch (message) {
\r
6328 case WM_INITDIALOG:
\r
6329 GetWindowRect(hDlg, &rChild);
\r
6331 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6335 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6336 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6337 and it doesn't work when you resize the dialog.
\r
6338 For now, just give it a default position.
\r
6340 gothicDialog = hDlg;
\r
6341 SetWindowText(hDlg, errorTitle);
\r
6342 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6343 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6347 switch (LOWORD(wParam)) {
\r
6350 if (errorDialog == hDlg) errorDialog = NULL;
\r
6351 DestroyWindow(hDlg);
\r
6363 GothicPopUp(char *title, VariantClass variant)
\r
6366 static char *lastTitle;
\r
6368 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6369 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6371 if(lastTitle != title && gothicDialog != NULL) {
\r
6372 DestroyWindow(gothicDialog);
\r
6373 gothicDialog = NULL;
\r
6375 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6376 title = lastTitle;
\r
6377 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6378 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6379 hwndMain, (DLGPROC)lpProc);
\r
6380 FreeProcInstance(lpProc);
\r
6385 /*---------------------------------------------------------------------------*\
\r
6387 * Ics Interaction console functions
\r
6389 \*---------------------------------------------------------------------------*/
\r
6391 #define HISTORY_SIZE 64
\r
6392 static char *history[HISTORY_SIZE];
\r
6393 int histIn = 0, histP = 0;
\r
6396 SaveInHistory(char *cmd)
\r
6398 if (history[histIn] != NULL) {
\r
6399 free(history[histIn]);
\r
6400 history[histIn] = NULL;
\r
6402 if (*cmd == NULLCHAR) return;
\r
6403 history[histIn] = StrSave(cmd);
\r
6404 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6405 if (history[histIn] != NULL) {
\r
6406 free(history[histIn]);
\r
6407 history[histIn] = NULL;
\r
6413 PrevInHistory(char *cmd)
\r
6416 if (histP == histIn) {
\r
6417 if (history[histIn] != NULL) free(history[histIn]);
\r
6418 history[histIn] = StrSave(cmd);
\r
6420 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6421 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6423 return history[histP];
\r
6429 if (histP == histIn) return NULL;
\r
6430 histP = (histP + 1) % HISTORY_SIZE;
\r
6431 return history[histP];
\r
6435 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6439 hmenu = LoadMenu(hInst, "TextMenu");
\r
6440 h = GetSubMenu(hmenu, 0);
\r
6442 if (strcmp(e->item, "-") == 0) {
\r
6443 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6444 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6445 int flags = MF_STRING, j = 0;
\r
6446 if (e->item[0] == '|') {
\r
6447 flags |= MF_MENUBARBREAK;
\r
6450 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6451 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6459 WNDPROC consoleTextWindowProc;
\r
6462 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6464 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6465 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6469 SetWindowText(hInput, command);
\r
6471 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6473 sel.cpMin = 999999;
\r
6474 sel.cpMax = 999999;
\r
6475 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6480 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6481 if (sel.cpMin == sel.cpMax) {
\r
6482 /* Expand to surrounding word */
\r
6485 tr.chrg.cpMax = sel.cpMin;
\r
6486 tr.chrg.cpMin = --sel.cpMin;
\r
6487 if (sel.cpMin < 0) break;
\r
6488 tr.lpstrText = name;
\r
6489 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6490 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6494 tr.chrg.cpMin = sel.cpMax;
\r
6495 tr.chrg.cpMax = ++sel.cpMax;
\r
6496 tr.lpstrText = name;
\r
6497 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6498 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6501 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6502 MessageBeep(MB_ICONEXCLAMATION);
\r
6506 tr.lpstrText = name;
\r
6507 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6509 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6510 MessageBeep(MB_ICONEXCLAMATION);
\r
6513 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6516 sprintf(buf, "%s %s", command, name);
\r
6517 SetWindowText(hInput, buf);
\r
6518 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6520 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6521 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6522 SetWindowText(hInput, buf);
\r
6523 sel.cpMin = 999999;
\r
6524 sel.cpMax = 999999;
\r
6525 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6531 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6536 switch (message) {
\r
6538 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6541 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6544 sel.cpMin = 999999;
\r
6545 sel.cpMax = 999999;
\r
6546 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6547 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6552 if(wParam != '\022') {
\r
6553 if (wParam == '\t') {
\r
6554 if (GetKeyState(VK_SHIFT) < 0) {
\r
6556 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6557 if (buttonDesc[0].hwnd) {
\r
6558 SetFocus(buttonDesc[0].hwnd);
\r
6560 SetFocus(hwndMain);
\r
6564 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6567 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6568 JAWS_DELETE( SetFocus(hInput); )
\r
6569 SendMessage(hInput, message, wParam, lParam);
\r
6572 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6573 case WM_RBUTTONDOWN:
\r
6574 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6575 /* Move selection here if it was empty */
\r
6577 pt.x = LOWORD(lParam);
\r
6578 pt.y = HIWORD(lParam);
\r
6579 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6580 if (sel.cpMin == sel.cpMax) {
\r
6581 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6582 sel.cpMax = sel.cpMin;
\r
6583 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6585 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6586 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6588 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6589 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6590 if (sel.cpMin == sel.cpMax) {
\r
6591 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6592 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6594 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6595 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6597 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6598 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6599 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6600 MenuPopup(hwnd, pt, hmenu, -1);
\r
6604 case WM_RBUTTONUP:
\r
6605 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6606 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6607 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6611 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6613 return SendMessage(hInput, message, wParam, lParam);
\r
6614 case WM_MBUTTONDOWN:
\r
6615 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6617 switch (LOWORD(wParam)) {
\r
6618 case IDM_QuickPaste:
\r
6620 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6621 if (sel.cpMin == sel.cpMax) {
\r
6622 MessageBeep(MB_ICONEXCLAMATION);
\r
6625 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6626 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6627 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6632 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6635 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6638 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6642 int i = LOWORD(wParam) - IDM_CommandX;
\r
6643 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6644 icsTextMenuEntry[i].command != NULL) {
\r
6645 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6646 icsTextMenuEntry[i].getname,
\r
6647 icsTextMenuEntry[i].immediate);
\r
6655 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6658 WNDPROC consoleInputWindowProc;
\r
6661 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6663 char buf[MSG_SIZ];
\r
6665 static BOOL sendNextChar = FALSE;
\r
6666 static BOOL quoteNextChar = FALSE;
\r
6667 InputSource *is = consoleInputSource;
\r
6671 switch (message) {
\r
6673 if (!appData.localLineEditing || sendNextChar) {
\r
6674 is->buf[0] = (CHAR) wParam;
\r
6676 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6677 sendNextChar = FALSE;
\r
6680 if (quoteNextChar) {
\r
6681 buf[0] = (char) wParam;
\r
6682 buf[1] = NULLCHAR;
\r
6683 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6684 quoteNextChar = FALSE;
\r
6688 case '\r': /* Enter key */
\r
6689 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6690 if (consoleEcho) SaveInHistory(is->buf);
\r
6691 is->buf[is->count++] = '\n';
\r
6692 is->buf[is->count] = NULLCHAR;
\r
6693 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6694 if (consoleEcho) {
\r
6695 ConsoleOutput(is->buf, is->count, TRUE);
\r
6696 } else if (appData.localLineEditing) {
\r
6697 ConsoleOutput("\n", 1, TRUE);
\r
6700 case '\033': /* Escape key */
\r
6701 SetWindowText(hwnd, "");
\r
6702 cf.cbSize = sizeof(CHARFORMAT);
\r
6703 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6704 if (consoleEcho) {
\r
6705 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6707 cf.crTextColor = COLOR_ECHOOFF;
\r
6709 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6710 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6712 case '\t': /* Tab key */
\r
6713 if (GetKeyState(VK_SHIFT) < 0) {
\r
6715 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6718 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6719 if (buttonDesc[0].hwnd) {
\r
6720 SetFocus(buttonDesc[0].hwnd);
\r
6722 SetFocus(hwndMain);
\r
6726 case '\023': /* Ctrl+S */
\r
6727 sendNextChar = TRUE;
\r
6729 case '\021': /* Ctrl+Q */
\r
6730 quoteNextChar = TRUE;
\r
6740 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6741 p = PrevInHistory(buf);
\r
6743 SetWindowText(hwnd, p);
\r
6744 sel.cpMin = 999999;
\r
6745 sel.cpMax = 999999;
\r
6746 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6751 p = NextInHistory();
\r
6753 SetWindowText(hwnd, p);
\r
6754 sel.cpMin = 999999;
\r
6755 sel.cpMax = 999999;
\r
6756 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6762 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6766 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6770 case WM_MBUTTONDOWN:
\r
6771 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6772 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6774 case WM_RBUTTONUP:
\r
6775 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6776 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6777 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6781 hmenu = LoadMenu(hInst, "InputMenu");
\r
6782 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6783 if (sel.cpMin == sel.cpMax) {
\r
6784 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6785 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6787 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6788 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6790 pt.x = LOWORD(lParam);
\r
6791 pt.y = HIWORD(lParam);
\r
6792 MenuPopup(hwnd, pt, hmenu, -1);
\r
6796 switch (LOWORD(wParam)) {
\r
6798 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6800 case IDM_SelectAll:
\r
6802 sel.cpMax = -1; /*999999?*/
\r
6803 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6806 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6809 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6812 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6817 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6820 #define CO_MAX 100000
\r
6821 #define CO_TRIM 1000
\r
6824 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6826 static SnapData sd;
\r
6827 HWND hText, hInput;
\r
6829 static int sizeX, sizeY;
\r
6830 int newSizeX, newSizeY;
\r
6834 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6835 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6837 switch (message) {
\r
6839 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6841 ENLINK *pLink = (ENLINK*)lParam;
\r
6842 if (pLink->msg == WM_LBUTTONUP)
\r
6846 tr.chrg = pLink->chrg;
\r
6847 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6848 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6849 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6850 free(tr.lpstrText);
\r
6854 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6855 hwndConsole = hDlg;
\r
6857 consoleTextWindowProc = (WNDPROC)
\r
6858 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6859 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6860 consoleInputWindowProc = (WNDPROC)
\r
6861 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6862 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6863 Colorize(ColorNormal, TRUE);
\r
6864 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6865 ChangedConsoleFont();
\r
6866 GetClientRect(hDlg, &rect);
\r
6867 sizeX = rect.right;
\r
6868 sizeY = rect.bottom;
\r
6869 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6870 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6871 WINDOWPLACEMENT wp;
\r
6872 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6873 wp.length = sizeof(WINDOWPLACEMENT);
\r
6875 wp.showCmd = SW_SHOW;
\r
6876 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6877 wp.rcNormalPosition.left = wpConsole.x;
\r
6878 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6879 wp.rcNormalPosition.top = wpConsole.y;
\r
6880 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6881 SetWindowPlacement(hDlg, &wp);
\r
6884 // [HGM] Chessknight's change 2004-07-13
\r
6885 else { /* Determine Defaults */
\r
6886 WINDOWPLACEMENT wp;
\r
6887 wpConsole.x = wpMain.width + 1;
\r
6888 wpConsole.y = wpMain.y;
\r
6889 wpConsole.width = screenWidth - wpMain.width;
\r
6890 wpConsole.height = wpMain.height;
\r
6891 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6892 wp.length = sizeof(WINDOWPLACEMENT);
\r
6894 wp.showCmd = SW_SHOW;
\r
6895 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6896 wp.rcNormalPosition.left = wpConsole.x;
\r
6897 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6898 wp.rcNormalPosition.top = wpConsole.y;
\r
6899 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6900 SetWindowPlacement(hDlg, &wp);
\r
6903 // Allow hText to highlight URLs and send notifications on them
\r
6904 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6905 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6906 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6907 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6921 if (IsIconic(hDlg)) break;
\r
6922 newSizeX = LOWORD(lParam);
\r
6923 newSizeY = HIWORD(lParam);
\r
6924 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6925 RECT rectText, rectInput;
\r
6927 int newTextHeight, newTextWidth;
\r
6928 GetWindowRect(hText, &rectText);
\r
6929 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6930 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6931 if (newTextHeight < 0) {
\r
6932 newSizeY += -newTextHeight;
\r
6933 newTextHeight = 0;
\r
6935 SetWindowPos(hText, NULL, 0, 0,
\r
6936 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6937 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6938 pt.x = rectInput.left;
\r
6939 pt.y = rectInput.top + newSizeY - sizeY;
\r
6940 ScreenToClient(hDlg, &pt);
\r
6941 SetWindowPos(hInput, NULL,
\r
6942 pt.x, pt.y, /* needs client coords */
\r
6943 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6944 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6950 case WM_GETMINMAXINFO:
\r
6951 /* Prevent resizing window too small */
\r
6952 mmi = (MINMAXINFO *) lParam;
\r
6953 mmi->ptMinTrackSize.x = 100;
\r
6954 mmi->ptMinTrackSize.y = 100;
\r
6957 /* [AS] Snapping */
\r
6958 case WM_ENTERSIZEMOVE:
\r
6959 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6962 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6965 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6967 case WM_EXITSIZEMOVE:
\r
6968 UpdateICSWidth(hText);
\r
6969 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6972 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6980 if (hwndConsole) return;
\r
6981 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6982 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6987 ConsoleOutput(char* data, int length, int forceVisible)
\r
6992 char buf[CO_MAX+1];
\r
6995 static int delayLF = 0;
\r
6996 CHARRANGE savesel, sel;
\r
6998 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7006 while (length--) {
\r
7014 } else if (*p == '\007') {
\r
7015 MyPlaySound(&sounds[(int)SoundBell]);
\r
7022 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7023 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7024 /* Save current selection */
\r
7025 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7026 exlen = GetWindowTextLength(hText);
\r
7027 /* Find out whether current end of text is visible */
\r
7028 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7029 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7030 /* Trim existing text if it's too long */
\r
7031 if (exlen + (q - buf) > CO_MAX) {
\r
7032 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7035 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7036 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7038 savesel.cpMin -= trim;
\r
7039 savesel.cpMax -= trim;
\r
7040 if (exlen < 0) exlen = 0;
\r
7041 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7042 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7044 /* Append the new text */
\r
7045 sel.cpMin = exlen;
\r
7046 sel.cpMax = exlen;
\r
7047 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7048 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7049 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7050 if (forceVisible || exlen == 0 ||
\r
7051 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7052 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7053 /* Scroll to make new end of text visible if old end of text
\r
7054 was visible or new text is an echo of user typein */
\r
7055 sel.cpMin = 9999999;
\r
7056 sel.cpMax = 9999999;
\r
7057 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7058 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7059 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7060 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7062 if (savesel.cpMax == exlen || forceVisible) {
\r
7063 /* Move insert point to new end of text if it was at the old
\r
7064 end of text or if the new text is an echo of user typein */
\r
7065 sel.cpMin = 9999999;
\r
7066 sel.cpMax = 9999999;
\r
7067 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7069 /* Restore previous selection */
\r
7070 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7072 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7079 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7083 COLORREF oldFg, oldBg;
\r
7087 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7089 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7090 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7091 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7094 rect.right = x + squareSize;
\r
7096 rect.bottom = y + squareSize;
\r
7099 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7100 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7101 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7102 &rect, str, strlen(str), NULL);
\r
7104 (void) SetTextColor(hdc, oldFg);
\r
7105 (void) SetBkColor(hdc, oldBg);
\r
7106 (void) SelectObject(hdc, oldFont);
\r
7110 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7111 RECT *rect, char *color, char *flagFell)
\r
7115 COLORREF oldFg, oldBg;
\r
7118 if (appData.clockMode) {
\r
7120 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7122 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7129 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7130 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7132 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7133 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7135 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7139 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7140 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7141 rect, str, strlen(str), NULL);
\r
7142 if(logoHeight > 0 && appData.clockMode) {
\r
7144 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7145 r.top = rect->top + logoHeight/2;
\r
7146 r.left = rect->left;
\r
7147 r.right = rect->right;
\r
7148 r.bottom = rect->bottom;
\r
7149 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7150 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7151 &r, str, strlen(str), NULL);
\r
7153 (void) SetTextColor(hdc, oldFg);
\r
7154 (void) SetBkColor(hdc, oldBg);
\r
7155 (void) SelectObject(hdc, oldFont);
\r
7160 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7166 if( count <= 0 ) {
\r
7167 if (appData.debugMode) {
\r
7168 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7171 return ERROR_INVALID_USER_BUFFER;
\r
7174 ResetEvent(ovl->hEvent);
\r
7175 ovl->Offset = ovl->OffsetHigh = 0;
\r
7176 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7180 err = GetLastError();
\r
7181 if (err == ERROR_IO_PENDING) {
\r
7182 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7186 err = GetLastError();
\r
7193 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7198 ResetEvent(ovl->hEvent);
\r
7199 ovl->Offset = ovl->OffsetHigh = 0;
\r
7200 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7204 err = GetLastError();
\r
7205 if (err == ERROR_IO_PENDING) {
\r
7206 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7210 err = GetLastError();
\r
7216 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7217 void CheckForInputBufferFull( InputSource * is )
\r
7219 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7220 /* Look for end of line */
\r
7221 char * p = is->buf;
\r
7223 while( p < is->next && *p != '\n' ) {
\r
7227 if( p >= is->next ) {
\r
7228 if (appData.debugMode) {
\r
7229 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7232 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7233 is->count = (DWORD) -1;
\r
7234 is->next = is->buf;
\r
7240 InputThread(LPVOID arg)
\r
7245 is = (InputSource *) arg;
\r
7246 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7247 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7248 while (is->hThread != NULL) {
\r
7249 is->error = DoReadFile(is->hFile, is->next,
\r
7250 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7251 &is->count, &ovl);
\r
7252 if (is->error == NO_ERROR) {
\r
7253 is->next += is->count;
\r
7255 if (is->error == ERROR_BROKEN_PIPE) {
\r
7256 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7259 is->count = (DWORD) -1;
\r
7260 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7265 CheckForInputBufferFull( is );
\r
7267 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7269 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7271 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7274 CloseHandle(ovl.hEvent);
\r
7275 CloseHandle(is->hFile);
\r
7277 if (appData.debugMode) {
\r
7278 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7285 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7287 NonOvlInputThread(LPVOID arg)
\r
7294 is = (InputSource *) arg;
\r
7295 while (is->hThread != NULL) {
\r
7296 is->error = ReadFile(is->hFile, is->next,
\r
7297 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7298 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7299 if (is->error == NO_ERROR) {
\r
7300 /* Change CRLF to LF */
\r
7301 if (is->next > is->buf) {
\r
7303 i = is->count + 1;
\r
7311 if (prev == '\r' && *p == '\n') {
\r
7323 if (is->error == ERROR_BROKEN_PIPE) {
\r
7324 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7327 is->count = (DWORD) -1;
\r
7331 CheckForInputBufferFull( is );
\r
7333 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7335 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7337 if (is->count < 0) break; /* Quit on error */
\r
7339 CloseHandle(is->hFile);
\r
7344 SocketInputThread(LPVOID arg)
\r
7348 is = (InputSource *) arg;
\r
7349 while (is->hThread != NULL) {
\r
7350 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7351 if ((int)is->count == SOCKET_ERROR) {
\r
7352 is->count = (DWORD) -1;
\r
7353 is->error = WSAGetLastError();
\r
7355 is->error = NO_ERROR;
\r
7356 is->next += is->count;
\r
7357 if (is->count == 0 && is->second == is) {
\r
7358 /* End of file on stderr; quit with no message */
\r
7362 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7364 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7366 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7372 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7376 is = (InputSource *) lParam;
\r
7377 if (is->lineByLine) {
\r
7378 /* Feed in lines one by one */
\r
7379 char *p = is->buf;
\r
7381 while (q < is->next) {
\r
7382 if (*q++ == '\n') {
\r
7383 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7388 /* Move any partial line to the start of the buffer */
\r
7390 while (p < is->next) {
\r
7395 if (is->error != NO_ERROR || is->count == 0) {
\r
7396 /* Notify backend of the error. Note: If there was a partial
\r
7397 line at the end, it is not flushed through. */
\r
7398 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7401 /* Feed in the whole chunk of input at once */
\r
7402 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7403 is->next = is->buf;
\r
7407 /*---------------------------------------------------------------------------*\
\r
7409 * Menu enables. Used when setting various modes.
\r
7411 \*---------------------------------------------------------------------------*/
\r
7419 GreyRevert(Boolean grey)
\r
7420 { // [HGM] vari: for retracting variations in local mode
\r
7421 HMENU hmenu = GetMenu(hwndMain);
\r
7422 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7423 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7427 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7429 while (enab->item > 0) {
\r
7430 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7435 Enables gnuEnables[] = {
\r
7436 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7437 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7439 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7440 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7441 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7442 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7443 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7444 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7445 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7446 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7447 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7448 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7452 Enables icsEnables[] = {
\r
7453 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7454 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7455 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7456 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7457 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7458 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7459 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7460 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7461 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7463 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7464 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7465 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7466 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7467 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7474 Enables zippyEnables[] = {
\r
7475 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7476 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7477 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7478 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7483 Enables ncpEnables[] = {
\r
7484 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7485 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7486 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7487 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7488 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7489 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7490 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7491 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7492 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7493 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7494 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7495 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7496 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7497 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7498 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7499 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7500 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7501 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7502 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7503 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7504 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7508 Enables trainingOnEnables[] = {
\r
7509 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7510 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7511 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7512 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7513 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7514 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7515 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7516 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7520 Enables trainingOffEnables[] = {
\r
7521 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7522 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7523 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7524 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7525 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7526 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7527 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7528 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7532 /* These modify either ncpEnables or gnuEnables */
\r
7533 Enables cmailEnables[] = {
\r
7534 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7535 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7536 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7537 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7538 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7539 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7540 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7544 Enables machineThinkingEnables[] = {
\r
7545 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7546 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7547 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7548 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7549 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7550 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7551 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7552 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7553 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7554 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7555 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7556 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7557 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7558 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7559 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7560 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7564 Enables userThinkingEnables[] = {
\r
7565 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7566 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7567 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7568 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7569 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7570 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7571 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7572 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7573 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7574 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7575 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7576 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7577 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7578 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7579 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7580 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7584 /*---------------------------------------------------------------------------*\
\r
7586 * Front-end interface functions exported by XBoard.
\r
7587 * Functions appear in same order as prototypes in frontend.h.
\r
7589 \*---------------------------------------------------------------------------*/
\r
7593 static UINT prevChecked = 0;
\r
7594 static int prevPausing = 0;
\r
7597 if (pausing != prevPausing) {
\r
7598 prevPausing = pausing;
\r
7599 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7600 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7601 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7604 switch (gameMode) {
\r
7605 case BeginningOfGame:
\r
7606 if (appData.icsActive)
\r
7607 nowChecked = IDM_IcsClient;
\r
7608 else if (appData.noChessProgram)
\r
7609 nowChecked = IDM_EditGame;
\r
7611 nowChecked = IDM_MachineBlack;
\r
7613 case MachinePlaysBlack:
\r
7614 nowChecked = IDM_MachineBlack;
\r
7616 case MachinePlaysWhite:
\r
7617 nowChecked = IDM_MachineWhite;
\r
7619 case TwoMachinesPlay:
\r
7620 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7623 nowChecked = IDM_AnalysisMode;
\r
7626 nowChecked = IDM_AnalyzeFile;
\r
7629 nowChecked = IDM_EditGame;
\r
7631 case PlayFromGameFile:
\r
7632 nowChecked = IDM_LoadGame;
\r
7634 case EditPosition:
\r
7635 nowChecked = IDM_EditPosition;
\r
7638 nowChecked = IDM_Training;
\r
7640 case IcsPlayingWhite:
\r
7641 case IcsPlayingBlack:
\r
7642 case IcsObserving:
\r
7644 nowChecked = IDM_IcsClient;
\r
7651 if (prevChecked != 0)
\r
7652 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7653 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7654 if (nowChecked != 0)
\r
7655 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7656 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7658 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7659 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7660 MF_BYCOMMAND|MF_ENABLED);
\r
7662 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7663 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7666 prevChecked = nowChecked;
\r
7668 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7669 if (appData.icsActive) {
\r
7670 if (appData.icsEngineAnalyze) {
\r
7671 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7672 MF_BYCOMMAND|MF_CHECKED);
\r
7674 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7675 MF_BYCOMMAND|MF_UNCHECKED);
\r
7683 HMENU hmenu = GetMenu(hwndMain);
\r
7684 SetMenuEnables(hmenu, icsEnables);
\r
7685 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7686 MF_BYPOSITION|MF_ENABLED);
\r
7688 if (appData.zippyPlay) {
\r
7689 SetMenuEnables(hmenu, zippyEnables);
\r
7690 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7691 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7692 MF_BYCOMMAND|MF_ENABLED);
\r
7700 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7706 HMENU hmenu = GetMenu(hwndMain);
\r
7707 SetMenuEnables(hmenu, ncpEnables);
\r
7708 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7709 MF_BYPOSITION|MF_GRAYED);
\r
7710 DrawMenuBar(hwndMain);
\r
7716 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7720 SetTrainingModeOn()
\r
7723 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7724 for (i = 0; i < N_BUTTONS; i++) {
\r
7725 if (buttonDesc[i].hwnd != NULL)
\r
7726 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7731 VOID SetTrainingModeOff()
\r
7734 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7735 for (i = 0; i < N_BUTTONS; i++) {
\r
7736 if (buttonDesc[i].hwnd != NULL)
\r
7737 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7743 SetUserThinkingEnables()
\r
7745 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7749 SetMachineThinkingEnables()
\r
7751 HMENU hMenu = GetMenu(hwndMain);
\r
7752 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7754 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7756 if (gameMode == MachinePlaysBlack) {
\r
7757 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7758 } else if (gameMode == MachinePlaysWhite) {
\r
7759 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7760 } else if (gameMode == TwoMachinesPlay) {
\r
7761 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
7767 DisplayTitle(char *str)
\r
7769 char title[MSG_SIZ], *host;
\r
7770 if (str[0] != NULLCHAR) {
\r
7771 strcpy(title, str);
\r
7772 } else if (appData.icsActive) {
\r
7773 if (appData.icsCommPort[0] != NULLCHAR)
\r
7776 host = appData.icsHost;
\r
7777 sprintf(title, "%s: %s", szTitle, host);
\r
7778 } else if (appData.noChessProgram) {
\r
7779 strcpy(title, szTitle);
\r
7781 strcpy(title, szTitle);
\r
7782 strcat(title, ": ");
\r
7783 strcat(title, first.tidy);
\r
7785 SetWindowText(hwndMain, title);
\r
7790 DisplayMessage(char *str1, char *str2)
\r
7794 int remain = MESSAGE_TEXT_MAX - 1;
\r
7797 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7798 messageText[0] = NULLCHAR;
\r
7800 len = strlen(str1);
\r
7801 if (len > remain) len = remain;
\r
7802 strncpy(messageText, str1, len);
\r
7803 messageText[len] = NULLCHAR;
\r
7806 if (*str2 && remain >= 2) {
\r
7808 strcat(messageText, " ");
\r
7811 len = strlen(str2);
\r
7812 if (len > remain) len = remain;
\r
7813 strncat(messageText, str2, len);
\r
7815 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7817 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7821 hdc = GetDC(hwndMain);
\r
7822 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7823 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7824 &messageRect, messageText, strlen(messageText), NULL);
\r
7825 (void) SelectObject(hdc, oldFont);
\r
7826 (void) ReleaseDC(hwndMain, hdc);
\r
7830 DisplayError(char *str, int error)
\r
7832 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7838 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7839 NULL, error, LANG_NEUTRAL,
\r
7840 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7842 sprintf(buf, "%s:\n%s", str, buf2);
\r
7844 ErrorMap *em = errmap;
\r
7845 while (em->err != 0 && em->err != error) em++;
\r
7846 if (em->err != 0) {
\r
7847 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7849 sprintf(buf, "%s:\nError code %d", str, error);
\r
7854 ErrorPopUp("Error", buf);
\r
7859 DisplayMoveError(char *str)
\r
7861 fromX = fromY = -1;
\r
7862 ClearHighlights();
\r
7863 DrawPosition(FALSE, NULL);
\r
7864 if (appData.popupMoveErrors) {
\r
7865 ErrorPopUp("Error", str);
\r
7867 DisplayMessage(str, "");
\r
7868 moveErrorMessageUp = TRUE;
\r
7873 DisplayFatalError(char *str, int error, int exitStatus)
\r
7875 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7877 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7880 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7881 NULL, error, LANG_NEUTRAL,
\r
7882 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7884 sprintf(buf, "%s:\n%s", str, buf2);
\r
7886 ErrorMap *em = errmap;
\r
7887 while (em->err != 0 && em->err != error) em++;
\r
7888 if (em->err != 0) {
\r
7889 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7891 sprintf(buf, "%s:\nError code %d", str, error);
\r
7896 if (appData.debugMode) {
\r
7897 fprintf(debugFP, "%s: %s\n", label, str);
\r
7899 if (appData.popupExitMessage) {
\r
7900 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7901 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7903 ExitEvent(exitStatus);
\r
7908 DisplayInformation(char *str)
\r
7910 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7915 DisplayNote(char *str)
\r
7917 ErrorPopUp("Note", str);
\r
7922 char *title, *question, *replyPrefix;
\r
7927 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7929 static QuestionParams *qp;
\r
7930 char reply[MSG_SIZ];
\r
7933 switch (message) {
\r
7934 case WM_INITDIALOG:
\r
7935 qp = (QuestionParams *) lParam;
\r
7936 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7937 SetWindowText(hDlg, qp->title);
\r
7938 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7939 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7943 switch (LOWORD(wParam)) {
\r
7945 strcpy(reply, qp->replyPrefix);
\r
7946 if (*reply) strcat(reply, " ");
\r
7947 len = strlen(reply);
\r
7948 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7949 strcat(reply, "\n");
\r
7950 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7951 EndDialog(hDlg, TRUE);
\r
7952 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7955 EndDialog(hDlg, FALSE);
\r
7966 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7968 QuestionParams qp;
\r
7972 qp.question = question;
\r
7973 qp.replyPrefix = replyPrefix;
\r
7975 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7976 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7977 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7978 FreeProcInstance(lpProc);
\r
7981 /* [AS] Pick FRC position */
\r
7982 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7984 static int * lpIndexFRC;
\r
7990 case WM_INITDIALOG:
\r
7991 lpIndexFRC = (int *) lParam;
\r
7993 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7995 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7996 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7997 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7998 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8003 switch( LOWORD(wParam) ) {
\r
8005 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8006 EndDialog( hDlg, 0 );
\r
8007 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8010 EndDialog( hDlg, 1 );
\r
8012 case IDC_NFG_Edit:
\r
8013 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8014 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8016 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8019 case IDC_NFG_Random:
\r
8020 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8021 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8034 int index = appData.defaultFrcPosition;
\r
8035 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8037 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8039 if( result == 0 ) {
\r
8040 appData.defaultFrcPosition = index;
\r
8046 /* [AS] Game list options. Refactored by HGM */
\r
8048 HWND gameListOptionsDialog;
\r
8050 // low-level front-end: clear text edit / list widget
\r
8054 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8057 // low-level front-end: clear text edit / list widget
\r
8059 GLT_DeSelectList()
\r
8061 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8064 // low-level front-end: append line to text edit / list widget
\r
8066 GLT_AddToList( char *name )
\r
8069 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8073 // low-level front-end: get line from text edit / list widget
\r
8075 GLT_GetFromList( int index, char *name )
\r
8078 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8084 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8086 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8087 int idx2 = idx1 + delta;
\r
8088 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8090 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8093 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8094 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8095 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8096 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8100 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8104 case WM_INITDIALOG:
\r
8105 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8107 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8109 /* Initialize list */
\r
8110 GLT_TagsToList( lpUserGLT );
\r
8112 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8117 switch( LOWORD(wParam) ) {
\r
8120 EndDialog( hDlg, 0 );
\r
8123 EndDialog( hDlg, 1 );
\r
8126 case IDC_GLT_Default:
\r
8127 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8130 case IDC_GLT_Restore:
\r
8131 GLT_TagsToList( appData.gameListTags );
\r
8135 GLT_MoveSelection( hDlg, -1 );
\r
8138 case IDC_GLT_Down:
\r
8139 GLT_MoveSelection( hDlg, +1 );
\r
8149 int GameListOptions()
\r
8152 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8154 strcpy( lpUserGLT, appData.gameListTags );
\r
8156 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8158 if( result == 0 ) {
\r
8159 /* [AS] Memory leak here! */
\r
8160 appData.gameListTags = strdup( lpUserGLT );
\r
8167 DisplayIcsInteractionTitle(char *str)
\r
8169 char consoleTitle[MSG_SIZ];
\r
8171 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8172 SetWindowText(hwndConsole, consoleTitle);
\r
8176 DrawPosition(int fullRedraw, Board board)
\r
8178 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8181 void NotifyFrontendLogin()
\r
8184 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8190 fromX = fromY = -1;
\r
8191 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8192 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8193 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8194 dragInfo.lastpos = dragInfo.pos;
\r
8195 dragInfo.start.x = dragInfo.start.y = -1;
\r
8196 dragInfo.from = dragInfo.start;
\r
8198 DrawPosition(TRUE, NULL);
\r
8205 CommentPopUp(char *title, char *str)
\r
8207 HWND hwnd = GetActiveWindow();
\r
8208 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8210 SetActiveWindow(hwnd);
\r
8214 CommentPopDown(void)
\r
8216 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8217 if (commentDialog) {
\r
8218 ShowWindow(commentDialog, SW_HIDE);
\r
8220 commentUp = FALSE;
\r
8224 EditCommentPopUp(int index, char *title, char *str)
\r
8226 EitherCommentPopUp(index, title, str, TRUE);
\r
8233 MyPlaySound(&sounds[(int)SoundMove]);
\r
8236 VOID PlayIcsWinSound()
\r
8238 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8241 VOID PlayIcsLossSound()
\r
8243 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8246 VOID PlayIcsDrawSound()
\r
8248 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8251 VOID PlayIcsUnfinishedSound()
\r
8253 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8259 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8267 consoleEcho = TRUE;
\r
8268 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8269 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8270 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8279 consoleEcho = FALSE;
\r
8280 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8281 /* This works OK: set text and background both to the same color */
\r
8283 cf.crTextColor = COLOR_ECHOOFF;
\r
8284 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8285 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8288 /* No Raw()...? */
\r
8290 void Colorize(ColorClass cc, int continuation)
\r
8292 currentColorClass = cc;
\r
8293 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8294 consoleCF.crTextColor = textAttribs[cc].color;
\r
8295 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8296 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8302 static char buf[MSG_SIZ];
\r
8303 DWORD bufsiz = MSG_SIZ;
\r
8305 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8306 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8308 if (!GetUserName(buf, &bufsiz)) {
\r
8309 /*DisplayError("Error getting user name", GetLastError());*/
\r
8310 strcpy(buf, "User");
\r
8318 static char buf[MSG_SIZ];
\r
8319 DWORD bufsiz = MSG_SIZ;
\r
8321 if (!GetComputerName(buf, &bufsiz)) {
\r
8322 /*DisplayError("Error getting host name", GetLastError());*/
\r
8323 strcpy(buf, "Unknown");
\r
8330 ClockTimerRunning()
\r
8332 return clockTimerEvent != 0;
\r
8338 if (clockTimerEvent == 0) return FALSE;
\r
8339 KillTimer(hwndMain, clockTimerEvent);
\r
8340 clockTimerEvent = 0;
\r
8345 StartClockTimer(long millisec)
\r
8347 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8348 (UINT) millisec, NULL);
\r
8352 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8355 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8357 if(appData.noGUI) return;
\r
8358 hdc = GetDC(hwndMain);
\r
8359 if (!IsIconic(hwndMain)) {
\r
8360 DisplayAClock(hdc, timeRemaining, highlight,
\r
8361 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8363 if (highlight && iconCurrent == iconBlack) {
\r
8364 iconCurrent = iconWhite;
\r
8365 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8366 if (IsIconic(hwndMain)) {
\r
8367 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8370 (void) ReleaseDC(hwndMain, hdc);
\r
8372 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8376 DisplayBlackClock(long timeRemaining, int highlight)
\r
8379 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8381 if(appData.noGUI) return;
\r
8382 hdc = GetDC(hwndMain);
\r
8383 if (!IsIconic(hwndMain)) {
\r
8384 DisplayAClock(hdc, timeRemaining, highlight,
\r
8385 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8387 if (highlight && iconCurrent == iconWhite) {
\r
8388 iconCurrent = iconBlack;
\r
8389 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8390 if (IsIconic(hwndMain)) {
\r
8391 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8394 (void) ReleaseDC(hwndMain, hdc);
\r
8396 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8401 LoadGameTimerRunning()
\r
8403 return loadGameTimerEvent != 0;
\r
8407 StopLoadGameTimer()
\r
8409 if (loadGameTimerEvent == 0) return FALSE;
\r
8410 KillTimer(hwndMain, loadGameTimerEvent);
\r
8411 loadGameTimerEvent = 0;
\r
8416 StartLoadGameTimer(long millisec)
\r
8418 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8419 (UINT) millisec, NULL);
\r
8427 char fileTitle[MSG_SIZ];
\r
8429 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8430 f = OpenFileDialog(hwndMain, "a", defName,
\r
8431 appData.oldSaveStyle ? "gam" : "pgn",
\r
8433 "Save Game to File", NULL, fileTitle, NULL);
\r
8435 SaveGame(f, 0, "");
\r
8442 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8444 if (delayedTimerEvent != 0) {
\r
8445 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8446 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8448 KillTimer(hwndMain, delayedTimerEvent);
\r
8449 delayedTimerEvent = 0;
\r
8450 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8451 delayedTimerCallback();
\r
8453 delayedTimerCallback = cb;
\r
8454 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8455 (UINT) millisec, NULL);
\r
8458 DelayedEventCallback
\r
8461 if (delayedTimerEvent) {
\r
8462 return delayedTimerCallback;
\r
8469 CancelDelayedEvent()
\r
8471 if (delayedTimerEvent) {
\r
8472 KillTimer(hwndMain, delayedTimerEvent);
\r
8473 delayedTimerEvent = 0;
\r
8477 DWORD GetWin32Priority(int nice)
\r
8478 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8480 REALTIME_PRIORITY_CLASS 0x00000100
\r
8481 HIGH_PRIORITY_CLASS 0x00000080
\r
8482 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8483 NORMAL_PRIORITY_CLASS 0x00000020
\r
8484 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8485 IDLE_PRIORITY_CLASS 0x00000040
\r
8487 if (nice < -15) return 0x00000080;
\r
8488 if (nice < 0) return 0x00008000;
\r
8489 if (nice == 0) return 0x00000020;
\r
8490 if (nice < 15) return 0x00004000;
\r
8491 return 0x00000040;
\r
8494 /* Start a child process running the given program.
\r
8495 The process's standard output can be read from "from", and its
\r
8496 standard input can be written to "to".
\r
8497 Exit with fatal error if anything goes wrong.
\r
8498 Returns an opaque pointer that can be used to destroy the process
\r
8502 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8504 #define BUFSIZE 4096
\r
8506 HANDLE hChildStdinRd, hChildStdinWr,
\r
8507 hChildStdoutRd, hChildStdoutWr;
\r
8508 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8509 SECURITY_ATTRIBUTES saAttr;
\r
8511 PROCESS_INFORMATION piProcInfo;
\r
8512 STARTUPINFO siStartInfo;
\r
8514 char buf[MSG_SIZ];
\r
8517 if (appData.debugMode) {
\r
8518 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8523 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8524 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8525 saAttr.bInheritHandle = TRUE;
\r
8526 saAttr.lpSecurityDescriptor = NULL;
\r
8529 * The steps for redirecting child's STDOUT:
\r
8530 * 1. Create anonymous pipe to be STDOUT for child.
\r
8531 * 2. Create a noninheritable duplicate of read handle,
\r
8532 * and close the inheritable read handle.
\r
8535 /* Create a pipe for the child's STDOUT. */
\r
8536 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8537 return GetLastError();
\r
8540 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8541 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8542 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8543 FALSE, /* not inherited */
\r
8544 DUPLICATE_SAME_ACCESS);
\r
8546 return GetLastError();
\r
8548 CloseHandle(hChildStdoutRd);
\r
8551 * The steps for redirecting child's STDIN:
\r
8552 * 1. Create anonymous pipe to be STDIN for child.
\r
8553 * 2. Create a noninheritable duplicate of write handle,
\r
8554 * and close the inheritable write handle.
\r
8557 /* Create a pipe for the child's STDIN. */
\r
8558 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8559 return GetLastError();
\r
8562 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8563 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8564 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8565 FALSE, /* not inherited */
\r
8566 DUPLICATE_SAME_ACCESS);
\r
8568 return GetLastError();
\r
8570 CloseHandle(hChildStdinWr);
\r
8572 /* Arrange to (1) look in dir for the child .exe file, and
\r
8573 * (2) have dir be the child's working directory. Interpret
\r
8574 * dir relative to the directory WinBoard loaded from. */
\r
8575 GetCurrentDirectory(MSG_SIZ, buf);
\r
8576 SetCurrentDirectory(installDir);
\r
8577 SetCurrentDirectory(dir);
\r
8579 /* Now create the child process. */
\r
8581 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8582 siStartInfo.lpReserved = NULL;
\r
8583 siStartInfo.lpDesktop = NULL;
\r
8584 siStartInfo.lpTitle = NULL;
\r
8585 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8586 siStartInfo.cbReserved2 = 0;
\r
8587 siStartInfo.lpReserved2 = NULL;
\r
8588 siStartInfo.hStdInput = hChildStdinRd;
\r
8589 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8590 siStartInfo.hStdError = hChildStdoutWr;
\r
8592 fSuccess = CreateProcess(NULL,
\r
8593 cmdLine, /* command line */
\r
8594 NULL, /* process security attributes */
\r
8595 NULL, /* primary thread security attrs */
\r
8596 TRUE, /* handles are inherited */
\r
8597 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8598 NULL, /* use parent's environment */
\r
8600 &siStartInfo, /* STARTUPINFO pointer */
\r
8601 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8603 err = GetLastError();
\r
8604 SetCurrentDirectory(buf); /* return to prev directory */
\r
8609 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8610 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8611 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8614 /* Close the handles we don't need in the parent */
\r
8615 CloseHandle(piProcInfo.hThread);
\r
8616 CloseHandle(hChildStdinRd);
\r
8617 CloseHandle(hChildStdoutWr);
\r
8619 /* Prepare return value */
\r
8620 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8621 cp->kind = CPReal;
\r
8622 cp->hProcess = piProcInfo.hProcess;
\r
8623 cp->pid = piProcInfo.dwProcessId;
\r
8624 cp->hFrom = hChildStdoutRdDup;
\r
8625 cp->hTo = hChildStdinWrDup;
\r
8627 *pr = (void *) cp;
\r
8629 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8630 2000 where engines sometimes don't see the initial command(s)
\r
8631 from WinBoard and hang. I don't understand how that can happen,
\r
8632 but the Sleep is harmless, so I've put it in. Others have also
\r
8633 reported what may be the same problem, so hopefully this will fix
\r
8634 it for them too. */
\r
8642 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8644 ChildProc *cp; int result;
\r
8646 cp = (ChildProc *) pr;
\r
8647 if (cp == NULL) return;
\r
8649 switch (cp->kind) {
\r
8651 /* TerminateProcess is considered harmful, so... */
\r
8652 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8653 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8654 /* The following doesn't work because the chess program
\r
8655 doesn't "have the same console" as WinBoard. Maybe
\r
8656 we could arrange for this even though neither WinBoard
\r
8657 nor the chess program uses a console for stdio? */
\r
8658 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8660 /* [AS] Special termination modes for misbehaving programs... */
\r
8661 if( signal == 9 ) {
\r
8662 result = TerminateProcess( cp->hProcess, 0 );
\r
8664 if ( appData.debugMode) {
\r
8665 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8668 else if( signal == 10 ) {
\r
8669 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8671 if( dw != WAIT_OBJECT_0 ) {
\r
8672 result = TerminateProcess( cp->hProcess, 0 );
\r
8674 if ( appData.debugMode) {
\r
8675 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8681 CloseHandle(cp->hProcess);
\r
8685 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8689 closesocket(cp->sock);
\r
8694 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8695 closesocket(cp->sock);
\r
8696 closesocket(cp->sock2);
\r
8704 InterruptChildProcess(ProcRef pr)
\r
8708 cp = (ChildProc *) pr;
\r
8709 if (cp == NULL) return;
\r
8710 switch (cp->kind) {
\r
8712 /* The following doesn't work because the chess program
\r
8713 doesn't "have the same console" as WinBoard. Maybe
\r
8714 we could arrange for this even though neither WinBoard
\r
8715 nor the chess program uses a console for stdio */
\r
8716 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8721 /* Can't interrupt */
\r
8725 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8732 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8734 char cmdLine[MSG_SIZ];
\r
8736 if (port[0] == NULLCHAR) {
\r
8737 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8739 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8741 return StartChildProcess(cmdLine, "", pr);
\r
8745 /* Code to open TCP sockets */
\r
8748 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8753 struct sockaddr_in sa, mysa;
\r
8754 struct hostent FAR *hp;
\r
8755 unsigned short uport;
\r
8756 WORD wVersionRequested;
\r
8759 /* Initialize socket DLL */
\r
8760 wVersionRequested = MAKEWORD(1, 1);
\r
8761 err = WSAStartup(wVersionRequested, &wsaData);
\r
8762 if (err != 0) return err;
\r
8765 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8766 err = WSAGetLastError();
\r
8771 /* Bind local address using (mostly) don't-care values.
\r
8773 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8774 mysa.sin_family = AF_INET;
\r
8775 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8776 uport = (unsigned short) 0;
\r
8777 mysa.sin_port = htons(uport);
\r
8778 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8779 == SOCKET_ERROR) {
\r
8780 err = WSAGetLastError();
\r
8785 /* Resolve remote host name */
\r
8786 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8787 if (!(hp = gethostbyname(host))) {
\r
8788 unsigned int b0, b1, b2, b3;
\r
8790 err = WSAGetLastError();
\r
8792 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8793 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8794 hp->h_addrtype = AF_INET;
\r
8796 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8797 hp->h_addr_list[0] = (char *) malloc(4);
\r
8798 hp->h_addr_list[0][0] = (char) b0;
\r
8799 hp->h_addr_list[0][1] = (char) b1;
\r
8800 hp->h_addr_list[0][2] = (char) b2;
\r
8801 hp->h_addr_list[0][3] = (char) b3;
\r
8807 sa.sin_family = hp->h_addrtype;
\r
8808 uport = (unsigned short) atoi(port);
\r
8809 sa.sin_port = htons(uport);
\r
8810 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8812 /* Make connection */
\r
8813 if (connect(s, (struct sockaddr *) &sa,
\r
8814 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8815 err = WSAGetLastError();
\r
8820 /* Prepare return value */
\r
8821 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8822 cp->kind = CPSock;
\r
8824 *pr = (ProcRef *) cp;
\r
8830 OpenCommPort(char *name, ProcRef *pr)
\r
8835 char fullname[MSG_SIZ];
\r
8837 if (*name != '\\')
\r
8838 sprintf(fullname, "\\\\.\\%s", name);
\r
8840 strcpy(fullname, name);
\r
8842 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8843 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8844 if (h == (HANDLE) -1) {
\r
8845 return GetLastError();
\r
8849 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8851 /* Accumulate characters until a 100ms pause, then parse */
\r
8852 ct.ReadIntervalTimeout = 100;
\r
8853 ct.ReadTotalTimeoutMultiplier = 0;
\r
8854 ct.ReadTotalTimeoutConstant = 0;
\r
8855 ct.WriteTotalTimeoutMultiplier = 0;
\r
8856 ct.WriteTotalTimeoutConstant = 0;
\r
8857 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8859 /* Prepare return value */
\r
8860 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8861 cp->kind = CPComm;
\r
8864 *pr = (ProcRef *) cp;
\r
8870 OpenLoopback(ProcRef *pr)
\r
8872 DisplayFatalError("Not implemented", 0, 1);
\r
8878 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8883 struct sockaddr_in sa, mysa;
\r
8884 struct hostent FAR *hp;
\r
8885 unsigned short uport;
\r
8886 WORD wVersionRequested;
\r
8889 char stderrPortStr[MSG_SIZ];
\r
8891 /* Initialize socket DLL */
\r
8892 wVersionRequested = MAKEWORD(1, 1);
\r
8893 err = WSAStartup(wVersionRequested, &wsaData);
\r
8894 if (err != 0) return err;
\r
8896 /* Resolve remote host name */
\r
8897 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8898 if (!(hp = gethostbyname(host))) {
\r
8899 unsigned int b0, b1, b2, b3;
\r
8901 err = WSAGetLastError();
\r
8903 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8904 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8905 hp->h_addrtype = AF_INET;
\r
8907 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8908 hp->h_addr_list[0] = (char *) malloc(4);
\r
8909 hp->h_addr_list[0][0] = (char) b0;
\r
8910 hp->h_addr_list[0][1] = (char) b1;
\r
8911 hp->h_addr_list[0][2] = (char) b2;
\r
8912 hp->h_addr_list[0][3] = (char) b3;
\r
8918 sa.sin_family = hp->h_addrtype;
\r
8919 uport = (unsigned short) 514;
\r
8920 sa.sin_port = htons(uport);
\r
8921 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8923 /* Bind local socket to unused "privileged" port address
\r
8925 s = INVALID_SOCKET;
\r
8926 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8927 mysa.sin_family = AF_INET;
\r
8928 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8929 for (fromPort = 1023;; fromPort--) {
\r
8930 if (fromPort < 0) {
\r
8932 return WSAEADDRINUSE;
\r
8934 if (s == INVALID_SOCKET) {
\r
8935 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8936 err = WSAGetLastError();
\r
8941 uport = (unsigned short) fromPort;
\r
8942 mysa.sin_port = htons(uport);
\r
8943 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8944 == SOCKET_ERROR) {
\r
8945 err = WSAGetLastError();
\r
8946 if (err == WSAEADDRINUSE) continue;
\r
8950 if (connect(s, (struct sockaddr *) &sa,
\r
8951 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8952 err = WSAGetLastError();
\r
8953 if (err == WSAEADDRINUSE) {
\r
8964 /* Bind stderr local socket to unused "privileged" port address
\r
8966 s2 = INVALID_SOCKET;
\r
8967 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8968 mysa.sin_family = AF_INET;
\r
8969 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8970 for (fromPort = 1023;; fromPort--) {
\r
8971 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8972 if (fromPort < 0) {
\r
8973 (void) closesocket(s);
\r
8975 return WSAEADDRINUSE;
\r
8977 if (s2 == INVALID_SOCKET) {
\r
8978 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8979 err = WSAGetLastError();
\r
8985 uport = (unsigned short) fromPort;
\r
8986 mysa.sin_port = htons(uport);
\r
8987 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8988 == SOCKET_ERROR) {
\r
8989 err = WSAGetLastError();
\r
8990 if (err == WSAEADDRINUSE) continue;
\r
8991 (void) closesocket(s);
\r
8995 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8996 err = WSAGetLastError();
\r
8997 if (err == WSAEADDRINUSE) {
\r
8999 s2 = INVALID_SOCKET;
\r
9002 (void) closesocket(s);
\r
9003 (void) closesocket(s2);
\r
9009 prevStderrPort = fromPort; // remember port used
\r
9010 sprintf(stderrPortStr, "%d", fromPort);
\r
9012 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9013 err = WSAGetLastError();
\r
9014 (void) closesocket(s);
\r
9015 (void) closesocket(s2);
\r
9020 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9021 err = WSAGetLastError();
\r
9022 (void) closesocket(s);
\r
9023 (void) closesocket(s2);
\r
9027 if (*user == NULLCHAR) user = UserName();
\r
9028 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9029 err = WSAGetLastError();
\r
9030 (void) closesocket(s);
\r
9031 (void) closesocket(s2);
\r
9035 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9036 err = WSAGetLastError();
\r
9037 (void) closesocket(s);
\r
9038 (void) closesocket(s2);
\r
9043 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9044 err = WSAGetLastError();
\r
9045 (void) closesocket(s);
\r
9046 (void) closesocket(s2);
\r
9050 (void) closesocket(s2); /* Stop listening */
\r
9052 /* Prepare return value */
\r
9053 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9054 cp->kind = CPRcmd;
\r
9057 *pr = (ProcRef *) cp;
\r
9064 AddInputSource(ProcRef pr, int lineByLine,
\r
9065 InputCallback func, VOIDSTAR closure)
\r
9067 InputSource *is, *is2 = NULL;
\r
9068 ChildProc *cp = (ChildProc *) pr;
\r
9070 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9071 is->lineByLine = lineByLine;
\r
9073 is->closure = closure;
\r
9074 is->second = NULL;
\r
9075 is->next = is->buf;
\r
9076 if (pr == NoProc) {
\r
9077 is->kind = CPReal;
\r
9078 consoleInputSource = is;
\r
9080 is->kind = cp->kind;
\r
9082 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9083 we create all threads suspended so that the is->hThread variable can be
\r
9084 safely assigned, then let the threads start with ResumeThread.
\r
9086 switch (cp->kind) {
\r
9088 is->hFile = cp->hFrom;
\r
9089 cp->hFrom = NULL; /* now owned by InputThread */
\r
9091 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9092 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9096 is->hFile = cp->hFrom;
\r
9097 cp->hFrom = NULL; /* now owned by InputThread */
\r
9099 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9100 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9104 is->sock = cp->sock;
\r
9106 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9107 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9111 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9113 is->sock = cp->sock;
\r
9115 is2->sock = cp->sock2;
\r
9116 is2->second = is2;
\r
9118 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9119 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9121 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9122 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9126 if( is->hThread != NULL ) {
\r
9127 ResumeThread( is->hThread );
\r
9130 if( is2 != NULL && is2->hThread != NULL ) {
\r
9131 ResumeThread( is2->hThread );
\r
9135 return (InputSourceRef) is;
\r
9139 RemoveInputSource(InputSourceRef isr)
\r
9143 is = (InputSource *) isr;
\r
9144 is->hThread = NULL; /* tell thread to stop */
\r
9145 CloseHandle(is->hThread);
\r
9146 if (is->second != NULL) {
\r
9147 is->second->hThread = NULL;
\r
9148 CloseHandle(is->second->hThread);
\r
9152 int no_wrap(char *message, int count)
\r
9154 ConsoleOutput(message, count, FALSE);
\r
9159 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9162 int outCount = SOCKET_ERROR;
\r
9163 ChildProc *cp = (ChildProc *) pr;
\r
9164 static OVERLAPPED ovl;
\r
9165 static int line = 0;
\r
9169 if (appData.noJoin || !appData.useInternalWrap)
\r
9170 return no_wrap(message, count);
\r
9173 int width = get_term_width();
\r
9174 int len = wrap(NULL, message, count, width, &line);
\r
9175 char *msg = malloc(len);
\r
9179 return no_wrap(message, count);
\r
9182 dbgchk = wrap(msg, message, count, width, &line);
\r
9183 if (dbgchk != len && appData.debugMode)
\r
9184 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9185 ConsoleOutput(msg, len, FALSE);
\r
9192 if (ovl.hEvent == NULL) {
\r
9193 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9195 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9197 switch (cp->kind) {
\r
9200 outCount = send(cp->sock, message, count, 0);
\r
9201 if (outCount == SOCKET_ERROR) {
\r
9202 *outError = WSAGetLastError();
\r
9204 *outError = NO_ERROR;
\r
9209 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9210 &dOutCount, NULL)) {
\r
9211 *outError = NO_ERROR;
\r
9212 outCount = (int) dOutCount;
\r
9214 *outError = GetLastError();
\r
9219 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9220 &dOutCount, &ovl);
\r
9221 if (*outError == NO_ERROR) {
\r
9222 outCount = (int) dOutCount;
\r
9230 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9233 /* Ignore delay, not implemented for WinBoard */
\r
9234 return OutputToProcess(pr, message, count, outError);
\r
9239 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9240 char *buf, int count, int error)
\r
9242 DisplayFatalError("Not implemented", 0, 1);
\r
9245 /* see wgamelist.c for Game List functions */
\r
9246 /* see wedittags.c for Edit Tags functions */
\r
9253 char buf[MSG_SIZ];
\r
9256 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9257 f = fopen(buf, "r");
\r
9259 ProcessICSInitScript(f);
\r
9267 StartAnalysisClock()
\r
9269 if (analysisTimerEvent) return;
\r
9270 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9271 (UINT) 2000, NULL);
\r
9275 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9277 highlightInfo.sq[0].x = fromX;
\r
9278 highlightInfo.sq[0].y = fromY;
\r
9279 highlightInfo.sq[1].x = toX;
\r
9280 highlightInfo.sq[1].y = toY;
\r
9286 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9287 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9291 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9293 premoveHighlightInfo.sq[0].x = fromX;
\r
9294 premoveHighlightInfo.sq[0].y = fromY;
\r
9295 premoveHighlightInfo.sq[1].x = toX;
\r
9296 premoveHighlightInfo.sq[1].y = toY;
\r
9300 ClearPremoveHighlights()
\r
9302 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9303 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9307 ShutDownFrontEnd()
\r
9309 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9310 DeleteClipboardTempFiles();
\r
9316 if (IsIconic(hwndMain))
\r
9317 ShowWindow(hwndMain, SW_RESTORE);
\r
9319 SetActiveWindow(hwndMain);
\r
9323 * Prototypes for animation support routines
\r
9325 static void ScreenSquare(int column, int row, POINT * pt);
\r
9326 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9327 POINT frames[], int * nFrames);
\r
9331 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9332 { // [HGM] atomic: animate blast wave
\r
9334 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9335 explodeInfo.fromX = fromX;
\r
9336 explodeInfo.fromY = fromY;
\r
9337 explodeInfo.toX = toX;
\r
9338 explodeInfo.toY = toY;
\r
9339 for(i=1; i<nFrames; i++) {
\r
9340 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9341 DrawPosition(FALSE, NULL);
\r
9342 Sleep(appData.animSpeed);
\r
9344 explodeInfo.radius = 0;
\r
9345 DrawPosition(TRUE, NULL);
\r
9351 AnimateMove(board, fromX, fromY, toX, toY)
\r
9358 ChessSquare piece;
\r
9359 POINT start, finish, mid;
\r
9360 POINT frames[kFactor * 2 + 1];
\r
9363 if (!appData.animate) return;
\r
9364 if (doingSizing) return;
\r
9365 if (fromY < 0 || fromX < 0) return;
\r
9366 piece = board[fromY][fromX];
\r
9367 if (piece >= EmptySquare) return;
\r
9369 ScreenSquare(fromX, fromY, &start);
\r
9370 ScreenSquare(toX, toY, &finish);
\r
9372 /* All pieces except knights move in straight line */
\r
9373 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9374 mid.x = start.x + (finish.x - start.x) / 2;
\r
9375 mid.y = start.y + (finish.y - start.y) / 2;
\r
9377 /* Knight: make diagonal movement then straight */
\r
9378 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9379 mid.x = start.x + (finish.x - start.x) / 2;
\r
9383 mid.y = start.y + (finish.y - start.y) / 2;
\r
9387 /* Don't use as many frames for very short moves */
\r
9388 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9389 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9391 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9393 animInfo.from.x = fromX;
\r
9394 animInfo.from.y = fromY;
\r
9395 animInfo.to.x = toX;
\r
9396 animInfo.to.y = toY;
\r
9397 animInfo.lastpos = start;
\r
9398 animInfo.piece = piece;
\r
9399 for (n = 0; n < nFrames; n++) {
\r
9400 animInfo.pos = frames[n];
\r
9401 DrawPosition(FALSE, NULL);
\r
9402 animInfo.lastpos = animInfo.pos;
\r
9403 Sleep(appData.animSpeed);
\r
9405 animInfo.pos = finish;
\r
9406 DrawPosition(FALSE, NULL);
\r
9407 animInfo.piece = EmptySquare;
\r
9408 if(gameInfo.variant == VariantAtomic &&
\r
9409 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9410 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9413 /* Convert board position to corner of screen rect and color */
\r
9416 ScreenSquare(column, row, pt)
\r
9417 int column; int row; POINT * pt;
\r
9420 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9421 pt->y = lineGap + row * (squareSize + lineGap);
\r
9423 pt->x = lineGap + column * (squareSize + lineGap);
\r
9424 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9428 /* Generate a series of frame coords from start->mid->finish.
\r
9429 The movement rate doubles until the half way point is
\r
9430 reached, then halves back down to the final destination,
\r
9431 which gives a nice slow in/out effect. The algorithmn
\r
9432 may seem to generate too many intermediates for short
\r
9433 moves, but remember that the purpose is to attract the
\r
9434 viewers attention to the piece about to be moved and
\r
9435 then to where it ends up. Too few frames would be less
\r
9439 Tween(start, mid, finish, factor, frames, nFrames)
\r
9440 POINT * start; POINT * mid;
\r
9441 POINT * finish; int factor;
\r
9442 POINT frames[]; int * nFrames;
\r
9444 int n, fraction = 1, count = 0;
\r
9446 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9447 for (n = 0; n < factor; n++)
\r
9449 for (n = 0; n < factor; n++) {
\r
9450 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9451 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9453 fraction = fraction / 2;
\r
9457 frames[count] = *mid;
\r
9460 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9462 for (n = 0; n < factor; n++) {
\r
9463 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9464 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9466 fraction = fraction * 2;
\r
9472 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9474 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9476 EvalGraphSet( first, last, current, pvInfoList );
\r