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], *p = name, *q;
\r
1226 if(name[0]== '%') {
\r
1227 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1228 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1230 *strchr(buf, '%') = 0;
\r
1231 strcat(fullname, getenv(buf));
\r
1232 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1234 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1235 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1236 return (int) strlen(fullname);
\r
1238 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1242 MyGetFullPathName(char *name, char *fullname)
\r
1245 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1250 { // [HGM] args: allows testing if main window is realized from back-end
\r
1251 return hwndMain != NULL;
\r
1255 PopUpStartupDialog()
\r
1259 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1260 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1261 FreeProcInstance(lpProc);
\r
1264 /*---------------------------------------------------------------------------*\
\r
1266 * GDI board drawing routines
\r
1268 \*---------------------------------------------------------------------------*/
\r
1270 /* [AS] Draw square using background texture */
\r
1271 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1276 return; /* Should never happen! */
\r
1279 SetGraphicsMode( dst, GM_ADVANCED );
\r
1286 /* X reflection */
\r
1291 x.eDx = (FLOAT) dw + dx - 1;
\r
1294 SetWorldTransform( dst, &x );
\r
1297 /* Y reflection */
\r
1303 x.eDy = (FLOAT) dh + dy - 1;
\r
1305 SetWorldTransform( dst, &x );
\r
1313 x.eDx = (FLOAT) dx;
\r
1314 x.eDy = (FLOAT) dy;
\r
1317 SetWorldTransform( dst, &x );
\r
1321 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1329 SetWorldTransform( dst, &x );
\r
1331 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1334 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1336 PM_WP = (int) WhitePawn,
\r
1337 PM_WN = (int) WhiteKnight,
\r
1338 PM_WB = (int) WhiteBishop,
\r
1339 PM_WR = (int) WhiteRook,
\r
1340 PM_WQ = (int) WhiteQueen,
\r
1341 PM_WF = (int) WhiteFerz,
\r
1342 PM_WW = (int) WhiteWazir,
\r
1343 PM_WE = (int) WhiteAlfil,
\r
1344 PM_WM = (int) WhiteMan,
\r
1345 PM_WO = (int) WhiteCannon,
\r
1346 PM_WU = (int) WhiteUnicorn,
\r
1347 PM_WH = (int) WhiteNightrider,
\r
1348 PM_WA = (int) WhiteAngel,
\r
1349 PM_WC = (int) WhiteMarshall,
\r
1350 PM_WAB = (int) WhiteCardinal,
\r
1351 PM_WD = (int) WhiteDragon,
\r
1352 PM_WL = (int) WhiteLance,
\r
1353 PM_WS = (int) WhiteCobra,
\r
1354 PM_WV = (int) WhiteFalcon,
\r
1355 PM_WSG = (int) WhiteSilver,
\r
1356 PM_WG = (int) WhiteGrasshopper,
\r
1357 PM_WK = (int) WhiteKing,
\r
1358 PM_BP = (int) BlackPawn,
\r
1359 PM_BN = (int) BlackKnight,
\r
1360 PM_BB = (int) BlackBishop,
\r
1361 PM_BR = (int) BlackRook,
\r
1362 PM_BQ = (int) BlackQueen,
\r
1363 PM_BF = (int) BlackFerz,
\r
1364 PM_BW = (int) BlackWazir,
\r
1365 PM_BE = (int) BlackAlfil,
\r
1366 PM_BM = (int) BlackMan,
\r
1367 PM_BO = (int) BlackCannon,
\r
1368 PM_BU = (int) BlackUnicorn,
\r
1369 PM_BH = (int) BlackNightrider,
\r
1370 PM_BA = (int) BlackAngel,
\r
1371 PM_BC = (int) BlackMarshall,
\r
1372 PM_BG = (int) BlackGrasshopper,
\r
1373 PM_BAB = (int) BlackCardinal,
\r
1374 PM_BD = (int) BlackDragon,
\r
1375 PM_BL = (int) BlackLance,
\r
1376 PM_BS = (int) BlackCobra,
\r
1377 PM_BV = (int) BlackFalcon,
\r
1378 PM_BSG = (int) BlackSilver,
\r
1379 PM_BK = (int) BlackKing
\r
1382 static HFONT hPieceFont = NULL;
\r
1383 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1384 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1385 static int fontBitmapSquareSize = 0;
\r
1386 static char pieceToFontChar[(int) EmptySquare] =
\r
1387 { 'p', 'n', 'b', 'r', 'q',
\r
1388 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1389 'k', 'o', 'm', 'v', 't', 'w',
\r
1390 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1393 extern BOOL SetCharTable( char *table, const char * map );
\r
1394 /* [HGM] moved to backend.c */
\r
1396 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1399 BYTE r1 = GetRValue( color );
\r
1400 BYTE g1 = GetGValue( color );
\r
1401 BYTE b1 = GetBValue( color );
\r
1407 /* Create a uniform background first */
\r
1408 hbrush = CreateSolidBrush( color );
\r
1409 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1410 FillRect( hdc, &rc, hbrush );
\r
1411 DeleteObject( hbrush );
\r
1414 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1415 int steps = squareSize / 2;
\r
1418 for( i=0; i<steps; i++ ) {
\r
1419 BYTE r = r1 - (r1-r2) * i / steps;
\r
1420 BYTE g = g1 - (g1-g2) * i / steps;
\r
1421 BYTE b = b1 - (b1-b2) * i / steps;
\r
1423 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1424 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1425 FillRect( hdc, &rc, hbrush );
\r
1426 DeleteObject(hbrush);
\r
1429 else if( mode == 2 ) {
\r
1430 /* Diagonal gradient, good more or less for every piece */
\r
1431 POINT triangle[3];
\r
1432 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1433 HBRUSH hbrush_old;
\r
1434 int steps = squareSize;
\r
1437 triangle[0].x = squareSize - steps;
\r
1438 triangle[0].y = squareSize;
\r
1439 triangle[1].x = squareSize;
\r
1440 triangle[1].y = squareSize;
\r
1441 triangle[2].x = squareSize;
\r
1442 triangle[2].y = squareSize - steps;
\r
1444 for( i=0; i<steps; i++ ) {
\r
1445 BYTE r = r1 - (r1-r2) * i / steps;
\r
1446 BYTE g = g1 - (g1-g2) * i / steps;
\r
1447 BYTE b = b1 - (b1-b2) * i / steps;
\r
1449 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1450 hbrush_old = SelectObject( hdc, hbrush );
\r
1451 Polygon( hdc, triangle, 3 );
\r
1452 SelectObject( hdc, hbrush_old );
\r
1453 DeleteObject(hbrush);
\r
1458 SelectObject( hdc, hpen );
\r
1463 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1464 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1465 piece: follow the steps as explained below.
\r
1467 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1471 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1475 int backColor = whitePieceColor;
\r
1476 int foreColor = blackPieceColor;
\r
1478 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1479 backColor = appData.fontBackColorWhite;
\r
1480 foreColor = appData.fontForeColorWhite;
\r
1482 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1483 backColor = appData.fontBackColorBlack;
\r
1484 foreColor = appData.fontForeColorBlack;
\r
1488 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1490 hbm_old = SelectObject( hdc, hbm );
\r
1494 rc.right = squareSize;
\r
1495 rc.bottom = squareSize;
\r
1497 /* Step 1: background is now black */
\r
1498 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1500 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1502 pt.x = (squareSize - sz.cx) / 2;
\r
1503 pt.y = (squareSize - sz.cy) / 2;
\r
1505 SetBkMode( hdc, TRANSPARENT );
\r
1506 SetTextColor( hdc, chroma );
\r
1507 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1508 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1510 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1511 /* Step 3: the area outside the piece is filled with white */
\r
1512 // FloodFill( hdc, 0, 0, chroma );
\r
1513 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1514 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1515 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1516 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1517 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1519 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1520 but if the start point is not inside the piece we're lost!
\r
1521 There should be a better way to do this... if we could create a region or path
\r
1522 from the fill operation we would be fine for example.
\r
1524 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1525 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1527 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1528 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1529 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1531 SelectObject( dc2, bm2 );
\r
1532 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1533 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1534 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1535 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1536 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1539 DeleteObject( bm2 );
\r
1542 SetTextColor( hdc, 0 );
\r
1544 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1545 draw the piece again in black for safety.
\r
1547 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1549 SelectObject( hdc, hbm_old );
\r
1551 if( hPieceMask[index] != NULL ) {
\r
1552 DeleteObject( hPieceMask[index] );
\r
1555 hPieceMask[index] = hbm;
\r
1558 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1560 SelectObject( hdc, hbm );
\r
1563 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1564 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1565 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1567 SelectObject( dc1, hPieceMask[index] );
\r
1568 SelectObject( dc2, bm2 );
\r
1569 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1570 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1573 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1574 the piece background and deletes (makes transparent) the rest.
\r
1575 Thanks to that mask, we are free to paint the background with the greates
\r
1576 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1577 We use this, to make gradients and give the pieces a "roundish" look.
\r
1579 SetPieceBackground( hdc, backColor, 2 );
\r
1580 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1584 DeleteObject( bm2 );
\r
1587 SetTextColor( hdc, foreColor );
\r
1588 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1590 SelectObject( hdc, hbm_old );
\r
1592 if( hPieceFace[index] != NULL ) {
\r
1593 DeleteObject( hPieceFace[index] );
\r
1596 hPieceFace[index] = hbm;
\r
1599 static int TranslatePieceToFontPiece( int piece )
\r
1629 case BlackMarshall:
\r
1633 case BlackNightrider:
\r
1639 case BlackUnicorn:
\r
1643 case BlackGrasshopper:
\r
1655 case BlackCardinal:
\r
1662 case WhiteMarshall:
\r
1666 case WhiteNightrider:
\r
1672 case WhiteUnicorn:
\r
1676 case WhiteGrasshopper:
\r
1688 case WhiteCardinal:
\r
1697 void CreatePiecesFromFont()
\r
1700 HDC hdc_window = NULL;
\r
1706 if( fontBitmapSquareSize < 0 ) {
\r
1707 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1711 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1712 fontBitmapSquareSize = -1;
\r
1716 if( fontBitmapSquareSize != squareSize ) {
\r
1717 hdc_window = GetDC( hwndMain );
\r
1718 hdc = CreateCompatibleDC( hdc_window );
\r
1720 if( hPieceFont != NULL ) {
\r
1721 DeleteObject( hPieceFont );
\r
1724 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1725 hPieceMask[i] = NULL;
\r
1726 hPieceFace[i] = NULL;
\r
1732 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1733 fontHeight = appData.fontPieceSize;
\r
1736 fontHeight = (fontHeight * squareSize) / 100;
\r
1738 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1740 lf.lfEscapement = 0;
\r
1741 lf.lfOrientation = 0;
\r
1742 lf.lfWeight = FW_NORMAL;
\r
1744 lf.lfUnderline = 0;
\r
1745 lf.lfStrikeOut = 0;
\r
1746 lf.lfCharSet = DEFAULT_CHARSET;
\r
1747 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1748 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1749 lf.lfQuality = PROOF_QUALITY;
\r
1750 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1751 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1752 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1754 hPieceFont = CreateFontIndirect( &lf );
\r
1756 if( hPieceFont == NULL ) {
\r
1757 fontBitmapSquareSize = -2;
\r
1760 /* Setup font-to-piece character table */
\r
1761 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1762 /* No (or wrong) global settings, try to detect the font */
\r
1763 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1765 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1767 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1768 /* DiagramTT* family */
\r
1769 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1771 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1772 /* Fairy symbols */
\r
1773 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1775 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1776 /* Good Companion (Some characters get warped as literal :-( */
\r
1777 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1778 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1779 SetCharTable(pieceToFontChar, s);
\r
1782 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1783 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1787 /* Create bitmaps */
\r
1788 hfont_old = SelectObject( hdc, hPieceFont );
\r
1789 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1790 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1791 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1793 SelectObject( hdc, hfont_old );
\r
1795 fontBitmapSquareSize = squareSize;
\r
1799 if( hdc != NULL ) {
\r
1803 if( hdc_window != NULL ) {
\r
1804 ReleaseDC( hwndMain, hdc_window );
\r
1809 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1813 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1814 if (gameInfo.event &&
\r
1815 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1816 strcmp(name, "k80s") == 0) {
\r
1817 strcpy(name, "tim");
\r
1819 return LoadBitmap(hinst, name);
\r
1823 /* Insert a color into the program's logical palette
\r
1824 structure. This code assumes the given color is
\r
1825 the result of the RGB or PALETTERGB macro, and it
\r
1826 knows how those macros work (which is documented).
\r
1829 InsertInPalette(COLORREF color)
\r
1831 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1833 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1834 DisplayFatalError("Too many colors", 0, 1);
\r
1835 pLogPal->palNumEntries--;
\r
1839 pe->peFlags = (char) 0;
\r
1840 pe->peRed = (char) (0xFF & color);
\r
1841 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1842 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1848 InitDrawingColors()
\r
1850 if (pLogPal == NULL) {
\r
1851 /* Allocate enough memory for a logical palette with
\r
1852 * PALETTESIZE entries and set the size and version fields
\r
1853 * of the logical palette structure.
\r
1855 pLogPal = (NPLOGPALETTE)
\r
1856 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1857 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1858 pLogPal->palVersion = 0x300;
\r
1860 pLogPal->palNumEntries = 0;
\r
1862 InsertInPalette(lightSquareColor);
\r
1863 InsertInPalette(darkSquareColor);
\r
1864 InsertInPalette(whitePieceColor);
\r
1865 InsertInPalette(blackPieceColor);
\r
1866 InsertInPalette(highlightSquareColor);
\r
1867 InsertInPalette(premoveHighlightColor);
\r
1869 /* create a logical color palette according the information
\r
1870 * in the LOGPALETTE structure.
\r
1872 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1874 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1875 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1876 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1877 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1878 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1879 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1880 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1881 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1882 /* [AS] Force rendering of the font-based pieces */
\r
1883 if( fontBitmapSquareSize > 0 ) {
\r
1884 fontBitmapSquareSize = 0;
\r
1890 BoardWidth(int boardSize, int n)
\r
1891 { /* [HGM] argument n added to allow different width and height */
\r
1892 int lineGap = sizeInfo[boardSize].lineGap;
\r
1894 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1895 lineGap = appData.overrideLineGap;
\r
1898 return (n + 1) * lineGap +
\r
1899 n * sizeInfo[boardSize].squareSize;
\r
1902 /* Respond to board resize by dragging edge */
\r
1904 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1906 BoardSize newSize = NUM_SIZES - 1;
\r
1907 static int recurse = 0;
\r
1908 if (IsIconic(hwndMain)) return;
\r
1909 if (recurse > 0) return;
\r
1911 while (newSize > 0) {
\r
1912 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1913 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1914 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1917 boardSize = newSize;
\r
1918 InitDrawingSizes(boardSize, flags);
\r
1923 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1926 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1928 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1929 ChessSquare piece;
\r
1930 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1932 SIZE clockSize, messageSize;
\r
1934 char buf[MSG_SIZ];
\r
1936 HMENU hmenu = GetMenu(hwndMain);
\r
1937 RECT crect, wrect, oldRect;
\r
1939 LOGBRUSH logbrush;
\r
1941 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1942 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1944 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1945 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1947 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1948 oldRect.top = wpMain.y;
\r
1949 oldRect.right = wpMain.x + wpMain.width;
\r
1950 oldRect.bottom = wpMain.y + wpMain.height;
\r
1952 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1953 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1954 squareSize = sizeInfo[boardSize].squareSize;
\r
1955 lineGap = sizeInfo[boardSize].lineGap;
\r
1956 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1958 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1959 lineGap = appData.overrideLineGap;
\r
1962 if (tinyLayout != oldTinyLayout) {
\r
1963 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1965 style &= ~WS_SYSMENU;
\r
1966 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1967 "&Minimize\tCtrl+F4");
\r
1969 style |= WS_SYSMENU;
\r
1970 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1972 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1974 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1975 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1976 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1978 DrawMenuBar(hwndMain);
\r
1981 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1982 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1984 /* Get text area sizes */
\r
1985 hdc = GetDC(hwndMain);
\r
1986 if (appData.clockMode) {
\r
1987 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1989 sprintf(buf, "White");
\r
1991 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1992 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1993 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1994 str = "We only care about the height here";
\r
1995 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1996 SelectObject(hdc, oldFont);
\r
1997 ReleaseDC(hwndMain, hdc);
\r
1999 /* Compute where everything goes */
\r
2000 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2001 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2002 logoHeight = 2*clockSize.cy;
\r
2003 leftLogoRect.left = OUTER_MARGIN;
\r
2004 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2005 leftLogoRect.top = OUTER_MARGIN;
\r
2006 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2008 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2009 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2010 rightLogoRect.top = OUTER_MARGIN;
\r
2011 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2014 whiteRect.left = leftLogoRect.right;
\r
2015 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2016 whiteRect.top = OUTER_MARGIN;
\r
2017 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2019 blackRect.right = rightLogoRect.left;
\r
2020 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2021 blackRect.top = whiteRect.top;
\r
2022 blackRect.bottom = whiteRect.bottom;
\r
2024 whiteRect.left = OUTER_MARGIN;
\r
2025 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2026 whiteRect.top = OUTER_MARGIN;
\r
2027 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2029 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2030 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2031 blackRect.top = whiteRect.top;
\r
2032 blackRect.bottom = whiteRect.bottom;
\r
2034 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2037 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2038 if (appData.showButtonBar) {
\r
2039 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2040 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2042 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2044 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2045 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2047 boardRect.left = OUTER_MARGIN;
\r
2048 boardRect.right = boardRect.left + boardWidth;
\r
2049 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2050 boardRect.bottom = boardRect.top + boardHeight;
\r
2052 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2053 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2054 oldBoardSize = boardSize;
\r
2055 oldTinyLayout = tinyLayout;
\r
2056 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2057 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2058 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2059 winW *= 1 + twoBoards;
\r
2060 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2061 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2062 wpMain.height = winH; // without disturbing window attachments
\r
2063 GetWindowRect(hwndMain, &wrect);
\r
2064 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2065 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2067 // [HGM] placement: let attached windows follow size change.
\r
2068 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2069 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2070 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2071 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2072 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2074 /* compensate if menu bar wrapped */
\r
2075 GetClientRect(hwndMain, &crect);
\r
2076 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2077 wpMain.height += offby;
\r
2079 case WMSZ_TOPLEFT:
\r
2080 SetWindowPos(hwndMain, NULL,
\r
2081 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2082 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2085 case WMSZ_TOPRIGHT:
\r
2087 SetWindowPos(hwndMain, NULL,
\r
2088 wrect.left, wrect.bottom - wpMain.height,
\r
2089 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2092 case WMSZ_BOTTOMLEFT:
\r
2094 SetWindowPos(hwndMain, NULL,
\r
2095 wrect.right - wpMain.width, wrect.top,
\r
2096 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2099 case WMSZ_BOTTOMRIGHT:
\r
2103 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2104 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2109 for (i = 0; i < N_BUTTONS; i++) {
\r
2110 if (buttonDesc[i].hwnd != NULL) {
\r
2111 DestroyWindow(buttonDesc[i].hwnd);
\r
2112 buttonDesc[i].hwnd = NULL;
\r
2114 if (appData.showButtonBar) {
\r
2115 buttonDesc[i].hwnd =
\r
2116 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2117 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2118 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2119 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2120 (HMENU) buttonDesc[i].id,
\r
2121 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2123 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2124 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2125 MAKELPARAM(FALSE, 0));
\r
2127 if (buttonDesc[i].id == IDM_Pause)
\r
2128 hwndPause = buttonDesc[i].hwnd;
\r
2129 buttonDesc[i].wndproc = (WNDPROC)
\r
2130 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2133 if (gridPen != NULL) DeleteObject(gridPen);
\r
2134 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2135 if (premovePen != NULL) DeleteObject(premovePen);
\r
2136 if (lineGap != 0) {
\r
2137 logbrush.lbStyle = BS_SOLID;
\r
2138 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2140 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2141 lineGap, &logbrush, 0, NULL);
\r
2142 logbrush.lbColor = highlightSquareColor;
\r
2144 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2145 lineGap, &logbrush, 0, NULL);
\r
2147 logbrush.lbColor = premoveHighlightColor;
\r
2149 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2150 lineGap, &logbrush, 0, NULL);
\r
2152 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2153 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2154 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2155 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2156 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2157 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2158 BOARD_WIDTH * (squareSize + lineGap);
\r
2159 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2161 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2162 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2163 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2164 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2165 lineGap / 2 + (i * (squareSize + lineGap));
\r
2166 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2167 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2168 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2172 /* [HGM] Licensing requirement */
\r
2174 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2177 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2179 GothicPopUp( "", VariantNormal);
\r
2182 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2184 /* Load piece bitmaps for this board size */
\r
2185 for (i=0; i<=2; i++) {
\r
2186 for (piece = WhitePawn;
\r
2187 (int) piece < (int) BlackPawn;
\r
2188 piece = (ChessSquare) ((int) piece + 1)) {
\r
2189 if (pieceBitmap[i][piece] != NULL)
\r
2190 DeleteObject(pieceBitmap[i][piece]);
\r
2194 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2195 // Orthodox Chess pieces
\r
2196 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2197 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2198 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2199 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2200 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2201 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2202 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2203 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2204 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2205 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2206 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2207 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2208 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2209 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2210 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2211 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2212 // in Shogi, Hijack the unused Queen for Lance
\r
2213 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2214 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2215 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2217 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2218 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2219 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2222 if(squareSize <= 72 && squareSize >= 33) {
\r
2223 /* A & C are available in most sizes now */
\r
2224 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2225 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2226 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2227 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2228 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2229 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2230 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2231 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2232 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2233 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2234 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2235 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2236 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2237 } else { // Smirf-like
\r
2238 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2242 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2243 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2244 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2245 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2246 } else { // WinBoard standard
\r
2247 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2248 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2249 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2254 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2255 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2256 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2257 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2258 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2259 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2260 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2261 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2262 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2263 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2264 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2265 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2266 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2267 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2268 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2269 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2270 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2271 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2272 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2273 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2274 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2275 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2276 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2277 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2278 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2279 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2280 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2281 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2282 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2286 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2287 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2288 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2289 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2290 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2291 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2292 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2293 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2294 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2295 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2296 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2297 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2298 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2300 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2301 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2302 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2303 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2304 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2305 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2306 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2307 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2308 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2309 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2310 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2311 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2314 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2315 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2316 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2317 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2318 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2319 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2320 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2321 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2322 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2323 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2324 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2325 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2326 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2327 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2328 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2332 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2333 /* special Shogi support in this size */
\r
2334 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2335 for (piece = WhitePawn;
\r
2336 (int) piece < (int) BlackPawn;
\r
2337 piece = (ChessSquare) ((int) piece + 1)) {
\r
2338 if (pieceBitmap[i][piece] != NULL)
\r
2339 DeleteObject(pieceBitmap[i][piece]);
\r
2342 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2344 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2345 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2346 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2347 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2348 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2349 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2350 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2351 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2352 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2353 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2354 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2355 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2356 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2358 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2359 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2360 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2361 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2362 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2363 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2364 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2365 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2366 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2367 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2368 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2369 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2370 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2372 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2373 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2374 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2375 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2376 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2377 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2378 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2379 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2380 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2381 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2382 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2383 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2389 PieceBitmap(ChessSquare p, int kind)
\r
2391 if ((int) p >= (int) BlackPawn)
\r
2392 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2394 return pieceBitmap[kind][(int) p];
\r
2397 /***************************************************************/
\r
2399 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2400 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2402 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2403 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2407 SquareToPos(int row, int column, int * x, int * y)
\r
2410 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2411 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2413 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2414 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2419 DrawCoordsOnDC(HDC hdc)
\r
2421 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
2422 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
2423 char str[2] = { NULLCHAR, NULLCHAR };
\r
2424 int oldMode, oldAlign, x, y, start, i;
\r
2428 if (!appData.showCoords)
\r
2431 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2433 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2434 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2435 oldAlign = GetTextAlign(hdc);
\r
2436 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2438 y = boardRect.top + lineGap;
\r
2439 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2441 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2442 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2443 str[0] = files[start + i];
\r
2444 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2445 y += squareSize + lineGap;
\r
2448 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2450 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2451 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2452 str[0] = ranks[start + i];
\r
2453 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2454 x += squareSize + lineGap;
\r
2457 SelectObject(hdc, oldBrush);
\r
2458 SetBkMode(hdc, oldMode);
\r
2459 SetTextAlign(hdc, oldAlign);
\r
2460 SelectObject(hdc, oldFont);
\r
2464 DrawGridOnDC(HDC hdc)
\r
2468 if (lineGap != 0) {
\r
2469 oldPen = SelectObject(hdc, gridPen);
\r
2470 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2471 SelectObject(hdc, oldPen);
\r
2475 #define HIGHLIGHT_PEN 0
\r
2476 #define PREMOVE_PEN 1
\r
2479 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2482 HPEN oldPen, hPen;
\r
2483 if (lineGap == 0) return;
\r
2485 x1 = boardRect.left +
\r
2486 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2487 y1 = boardRect.top +
\r
2488 lineGap/2 + y * (squareSize + lineGap);
\r
2490 x1 = boardRect.left +
\r
2491 lineGap/2 + x * (squareSize + lineGap);
\r
2492 y1 = boardRect.top +
\r
2493 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2495 hPen = pen ? premovePen : highlightPen;
\r
2496 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2497 MoveToEx(hdc, x1, y1, NULL);
\r
2498 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2499 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2500 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2501 LineTo(hdc, x1, y1);
\r
2502 SelectObject(hdc, oldPen);
\r
2506 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2509 for (i=0; i<2; i++) {
\r
2510 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2511 DrawHighlightOnDC(hdc, TRUE,
\r
2512 h->sq[i].x, h->sq[i].y,
\r
2517 /* Note: sqcolor is used only in monoMode */
\r
2518 /* Note that this code is largely duplicated in woptions.c,
\r
2519 function DrawSampleSquare, so that needs to be updated too */
\r
2521 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2523 HBITMAP oldBitmap;
\r
2527 if (appData.blindfold) return;
\r
2529 /* [AS] Use font-based pieces if needed */
\r
2530 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2531 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2532 CreatePiecesFromFont();
\r
2534 if( fontBitmapSquareSize == squareSize ) {
\r
2535 int index = TranslatePieceToFontPiece(piece);
\r
2537 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2539 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2540 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2544 squareSize, squareSize,
\r
2549 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2551 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2552 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2556 squareSize, squareSize,
\r
2565 if (appData.monoMode) {
\r
2566 SelectObject(tmphdc, PieceBitmap(piece,
\r
2567 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2568 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2569 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2571 tmpSize = squareSize;
\r
2573 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2574 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2575 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2576 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2577 x += (squareSize - minorSize)>>1;
\r
2578 y += squareSize - minorSize - 2;
\r
2579 tmpSize = minorSize;
\r
2581 if (color || appData.allWhite ) {
\r
2582 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2584 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2585 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2586 if(appData.upsideDown && color==flipView)
\r
2587 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2589 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2590 /* Use black for outline of white pieces */
\r
2591 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2592 if(appData.upsideDown && color==flipView)
\r
2593 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2595 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2597 /* Use square color for details of black pieces */
\r
2598 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2599 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2600 if(appData.upsideDown && !flipView)
\r
2601 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2603 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2605 SelectObject(hdc, oldBrush);
\r
2606 SelectObject(tmphdc, oldBitmap);
\r
2610 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2611 int GetBackTextureMode( int algo )
\r
2613 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2617 case BACK_TEXTURE_MODE_PLAIN:
\r
2618 result = 1; /* Always use identity map */
\r
2620 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2621 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2629 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2630 to handle redraws cleanly (as random numbers would always be different).
\r
2632 VOID RebuildTextureSquareInfo()
\r
2642 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2644 if( liteBackTexture != NULL ) {
\r
2645 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2646 lite_w = bi.bmWidth;
\r
2647 lite_h = bi.bmHeight;
\r
2651 if( darkBackTexture != NULL ) {
\r
2652 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2653 dark_w = bi.bmWidth;
\r
2654 dark_h = bi.bmHeight;
\r
2658 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2659 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2660 if( (col + row) & 1 ) {
\r
2662 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2663 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2664 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2666 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2667 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2668 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2670 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2671 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2676 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2677 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2678 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2680 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2681 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2682 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2684 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2685 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2692 /* [AS] Arrow highlighting support */
\r
2694 static int A_WIDTH = 5; /* Width of arrow body */
\r
2696 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2697 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2699 static double Sqr( double x )
\r
2704 static int Round( double x )
\r
2706 return (int) (x + 0.5);
\r
2709 /* Draw an arrow between two points using current settings */
\r
2710 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2713 double dx, dy, j, k, x, y;
\r
2715 if( d_x == s_x ) {
\r
2716 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2718 arrow[0].x = s_x + A_WIDTH;
\r
2721 arrow[1].x = s_x + A_WIDTH;
\r
2722 arrow[1].y = d_y - h;
\r
2724 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2725 arrow[2].y = d_y - h;
\r
2730 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2731 arrow[4].y = d_y - h;
\r
2733 arrow[5].x = s_x - A_WIDTH;
\r
2734 arrow[5].y = d_y - h;
\r
2736 arrow[6].x = s_x - A_WIDTH;
\r
2739 else if( d_y == s_y ) {
\r
2740 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2743 arrow[0].y = s_y + A_WIDTH;
\r
2745 arrow[1].x = d_x - w;
\r
2746 arrow[1].y = s_y + A_WIDTH;
\r
2748 arrow[2].x = d_x - w;
\r
2749 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2754 arrow[4].x = d_x - w;
\r
2755 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2757 arrow[5].x = d_x - w;
\r
2758 arrow[5].y = s_y - A_WIDTH;
\r
2761 arrow[6].y = s_y - A_WIDTH;
\r
2764 /* [AS] Needed a lot of paper for this! :-) */
\r
2765 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2766 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2768 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2770 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2775 arrow[0].x = Round(x - j);
\r
2776 arrow[0].y = Round(y + j*dx);
\r
2778 arrow[1].x = Round(x + j);
\r
2779 arrow[1].y = Round(y - j*dx);
\r
2782 x = (double) d_x - k;
\r
2783 y = (double) d_y - k*dy;
\r
2786 x = (double) d_x + k;
\r
2787 y = (double) d_y + k*dy;
\r
2790 arrow[2].x = Round(x + j);
\r
2791 arrow[2].y = Round(y - j*dx);
\r
2793 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2794 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2799 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2800 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2802 arrow[6].x = Round(x - j);
\r
2803 arrow[6].y = Round(y + j*dx);
\r
2806 Polygon( hdc, arrow, 7 );
\r
2809 /* [AS] Draw an arrow between two squares */
\r
2810 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2812 int s_x, s_y, d_x, d_y;
\r
2819 if( s_col == d_col && s_row == d_row ) {
\r
2823 /* Get source and destination points */
\r
2824 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2825 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2828 d_y += squareSize / 4;
\r
2830 else if( d_y < s_y ) {
\r
2831 d_y += 3 * squareSize / 4;
\r
2834 d_y += squareSize / 2;
\r
2838 d_x += squareSize / 4;
\r
2840 else if( d_x < s_x ) {
\r
2841 d_x += 3 * squareSize / 4;
\r
2844 d_x += squareSize / 2;
\r
2847 s_x += squareSize / 2;
\r
2848 s_y += squareSize / 2;
\r
2850 /* Adjust width */
\r
2851 A_WIDTH = squareSize / 14;
\r
2854 stLB.lbStyle = BS_SOLID;
\r
2855 stLB.lbColor = appData.highlightArrowColor;
\r
2858 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2859 holdpen = SelectObject( hdc, hpen );
\r
2860 hbrush = CreateBrushIndirect( &stLB );
\r
2861 holdbrush = SelectObject( hdc, hbrush );
\r
2863 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2865 SelectObject( hdc, holdpen );
\r
2866 SelectObject( hdc, holdbrush );
\r
2867 DeleteObject( hpen );
\r
2868 DeleteObject( hbrush );
\r
2871 BOOL HasHighlightInfo()
\r
2873 BOOL result = FALSE;
\r
2875 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2876 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2884 BOOL IsDrawArrowEnabled()
\r
2886 BOOL result = FALSE;
\r
2888 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2895 VOID DrawArrowHighlight( HDC hdc )
\r
2897 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2898 DrawArrowBetweenSquares( hdc,
\r
2899 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2900 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2904 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2906 HRGN result = NULL;
\r
2908 if( HasHighlightInfo() ) {
\r
2909 int x1, y1, x2, y2;
\r
2910 int sx, sy, dx, dy;
\r
2912 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2913 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2915 sx = MIN( x1, x2 );
\r
2916 sy = MIN( y1, y2 );
\r
2917 dx = MAX( x1, x2 ) + squareSize;
\r
2918 dy = MAX( y1, y2 ) + squareSize;
\r
2920 result = CreateRectRgn( sx, sy, dx, dy );
\r
2927 Warning: this function modifies the behavior of several other functions.
\r
2929 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2930 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2931 repaint is scattered all over the place, which is not good for features such as
\r
2932 "arrow highlighting" that require a full repaint of the board.
\r
2934 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2935 user interaction, when speed is not so important) but especially to avoid errors
\r
2936 in the displayed graphics.
\r
2938 In such patched places, I always try refer to this function so there is a single
\r
2939 place to maintain knowledge.
\r
2941 To restore the original behavior, just return FALSE unconditionally.
\r
2943 BOOL IsFullRepaintPreferrable()
\r
2945 BOOL result = FALSE;
\r
2947 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2948 /* Arrow may appear on the board */
\r
2956 This function is called by DrawPosition to know whether a full repaint must
\r
2959 Only DrawPosition may directly call this function, which makes use of
\r
2960 some state information. Other function should call DrawPosition specifying
\r
2961 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2963 BOOL DrawPositionNeedsFullRepaint()
\r
2965 BOOL result = FALSE;
\r
2968 Probably a slightly better policy would be to trigger a full repaint
\r
2969 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2970 but animation is fast enough that it's difficult to notice.
\r
2972 if( animInfo.piece == EmptySquare ) {
\r
2973 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2982 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2984 int row, column, x, y, square_color, piece_color;
\r
2985 ChessSquare piece;
\r
2987 HDC texture_hdc = NULL;
\r
2989 /* [AS] Initialize background textures if needed */
\r
2990 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2991 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2992 if( backTextureSquareSize != squareSize
\r
2993 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2994 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2995 backTextureSquareSize = squareSize;
\r
2996 RebuildTextureSquareInfo();
\r
2999 texture_hdc = CreateCompatibleDC( hdc );
\r
3002 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3003 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3005 SquareToPos(row, column, &x, &y);
\r
3007 piece = board[row][column];
\r
3009 square_color = ((column + row) % 2) == 1;
\r
3010 if( gameInfo.variant == VariantXiangqi ) {
\r
3011 square_color = !InPalace(row, column);
\r
3012 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3013 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3015 piece_color = (int) piece < (int) BlackPawn;
\r
3018 /* [HGM] holdings file: light square or black */
\r
3019 if(column == BOARD_LEFT-2) {
\r
3020 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3023 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3027 if(column == BOARD_RGHT + 1 ) {
\r
3028 if( row < gameInfo.holdingsSize )
\r
3031 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3035 if(column == BOARD_LEFT-1 ) /* left align */
\r
3036 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3037 else if( column == BOARD_RGHT) /* right align */
\r
3038 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3040 if (appData.monoMode) {
\r
3041 if (piece == EmptySquare) {
\r
3042 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3043 square_color ? WHITENESS : BLACKNESS);
\r
3045 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3048 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3049 /* [AS] Draw the square using a texture bitmap */
\r
3050 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3051 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3052 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3055 squareSize, squareSize,
\r
3058 backTextureSquareInfo[r][c].mode,
\r
3059 backTextureSquareInfo[r][c].x,
\r
3060 backTextureSquareInfo[r][c].y );
\r
3062 SelectObject( texture_hdc, hbm );
\r
3064 if (piece != EmptySquare) {
\r
3065 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3069 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3071 oldBrush = SelectObject(hdc, brush );
\r
3072 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3073 SelectObject(hdc, oldBrush);
\r
3074 if (piece != EmptySquare)
\r
3075 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3080 if( texture_hdc != NULL ) {
\r
3081 DeleteDC( texture_hdc );
\r
3085 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3086 void fputDW(FILE *f, int x)
\r
3088 fputc(x & 255, f);
\r
3089 fputc(x>>8 & 255, f);
\r
3090 fputc(x>>16 & 255, f);
\r
3091 fputc(x>>24 & 255, f);
\r
3094 #define MAX_CLIPS 200 /* more than enough */
\r
3097 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3099 // HBITMAP bufferBitmap;
\r
3104 int w = 100, h = 50;
\r
3106 if(logo == NULL) return;
\r
3107 // GetClientRect(hwndMain, &Rect);
\r
3108 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3109 // Rect.bottom-Rect.top+1);
\r
3110 tmphdc = CreateCompatibleDC(hdc);
\r
3111 hbm = SelectObject(tmphdc, logo);
\r
3112 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3116 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3117 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3118 SelectObject(tmphdc, hbm);
\r
3122 static HDC hdcSeek;
\r
3124 // [HGM] seekgraph
\r
3125 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3128 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3129 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3130 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3131 SelectObject( hdcSeek, hp );
\r
3134 // front-end wrapper for drawing functions to do rectangles
\r
3135 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3140 if (hdcSeek == NULL) {
\r
3141 hdcSeek = GetDC(hwndMain);
\r
3142 if (!appData.monoMode) {
\r
3143 SelectPalette(hdcSeek, hPal, FALSE);
\r
3144 RealizePalette(hdcSeek);
\r
3147 hp = SelectObject( hdcSeek, gridPen );
\r
3148 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3149 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3150 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3151 SelectObject( hdcSeek, hp );
\r
3154 // front-end wrapper for putting text in graph
\r
3155 void DrawSeekText(char *buf, int x, int y)
\r
3158 SetBkMode( hdcSeek, TRANSPARENT );
\r
3159 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3160 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3163 void DrawSeekDot(int x, int y, int color)
\r
3165 int square = color & 0x80;
\r
3166 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3167 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3170 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3171 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3173 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3174 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3175 SelectObject(hdcSeek, oldBrush);
\r
3179 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3181 static Board lastReq[2], lastDrawn[2];
\r
3182 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3183 static int lastDrawnFlipView = 0;
\r
3184 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3185 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3188 HBITMAP bufferBitmap;
\r
3189 HBITMAP oldBitmap;
\r
3191 HRGN clips[MAX_CLIPS];
\r
3192 ChessSquare dragged_piece = EmptySquare;
\r
3193 int nr = twoBoards*partnerUp;
\r
3195 /* I'm undecided on this - this function figures out whether a full
\r
3196 * repaint is necessary on its own, so there's no real reason to have the
\r
3197 * caller tell it that. I think this can safely be set to FALSE - but
\r
3198 * if we trust the callers not to request full repaints unnessesarily, then
\r
3199 * we could skip some clipping work. In other words, only request a full
\r
3200 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3201 * gamestart and similar) --Hawk
\r
3203 Boolean fullrepaint = repaint;
\r
3205 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3207 if( DrawPositionNeedsFullRepaint() ) {
\r
3208 fullrepaint = TRUE;
\r
3211 if (board == NULL) {
\r
3212 if (!lastReqValid[nr]) {
\r
3215 board = lastReq[nr];
\r
3217 CopyBoard(lastReq[nr], board);
\r
3218 lastReqValid[nr] = 1;
\r
3221 if (doingSizing) {
\r
3225 if (IsIconic(hwndMain)) {
\r
3229 if (hdc == NULL) {
\r
3230 hdc = GetDC(hwndMain);
\r
3231 if (!appData.monoMode) {
\r
3232 SelectPalette(hdc, hPal, FALSE);
\r
3233 RealizePalette(hdc);
\r
3237 releaseDC = FALSE;
\r
3240 /* Create some work-DCs */
\r
3241 hdcmem = CreateCompatibleDC(hdc);
\r
3242 tmphdc = CreateCompatibleDC(hdc);
\r
3244 /* If dragging is in progress, we temporarely remove the piece */
\r
3245 /* [HGM] or temporarily decrease count if stacked */
\r
3246 /* !! Moved to before board compare !! */
\r
3247 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3248 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3249 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3250 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3251 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3253 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3254 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3255 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3257 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3260 /* Figure out which squares need updating by comparing the
\r
3261 * newest board with the last drawn board and checking if
\r
3262 * flipping has changed.
\r
3264 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3265 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3266 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3267 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3268 SquareToPos(row, column, &x, &y);
\r
3269 clips[num_clips++] =
\r
3270 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3274 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3275 for (i=0; i<2; i++) {
\r
3276 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3277 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3278 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3279 lastDrawnHighlight.sq[i].y >= 0) {
\r
3280 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3281 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3282 clips[num_clips++] =
\r
3283 CreateRectRgn(x - lineGap, y - lineGap,
\r
3284 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3286 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3287 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3288 clips[num_clips++] =
\r
3289 CreateRectRgn(x - lineGap, y - lineGap,
\r
3290 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3294 for (i=0; i<2; i++) {
\r
3295 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3296 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3297 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3298 lastDrawnPremove.sq[i].y >= 0) {
\r
3299 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3300 lastDrawnPremove.sq[i].x, &x, &y);
\r
3301 clips[num_clips++] =
\r
3302 CreateRectRgn(x - lineGap, y - lineGap,
\r
3303 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3305 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3306 premoveHighlightInfo.sq[i].y >= 0) {
\r
3307 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3308 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3309 clips[num_clips++] =
\r
3310 CreateRectRgn(x - lineGap, y - lineGap,
\r
3311 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3315 } else { // nr == 1
\r
3316 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3317 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3318 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3319 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3320 for (i=0; i<2; i++) {
\r
3321 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3322 partnerHighlightInfo.sq[i].y >= 0) {
\r
3323 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3324 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3325 clips[num_clips++] =
\r
3326 CreateRectRgn(x - lineGap, y - lineGap,
\r
3327 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3329 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3330 oldPartnerHighlight.sq[i].y >= 0) {
\r
3331 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3332 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3333 clips[num_clips++] =
\r
3334 CreateRectRgn(x - lineGap, y - lineGap,
\r
3335 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3340 fullrepaint = TRUE;
\r
3343 /* Create a buffer bitmap - this is the actual bitmap
\r
3344 * being written to. When all the work is done, we can
\r
3345 * copy it to the real DC (the screen). This avoids
\r
3346 * the problems with flickering.
\r
3348 GetClientRect(hwndMain, &Rect);
\r
3349 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3350 Rect.bottom-Rect.top+1);
\r
3351 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3352 if (!appData.monoMode) {
\r
3353 SelectPalette(hdcmem, hPal, FALSE);
\r
3356 /* Create clips for dragging */
\r
3357 if (!fullrepaint) {
\r
3358 if (dragInfo.from.x >= 0) {
\r
3359 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3360 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3362 if (dragInfo.start.x >= 0) {
\r
3363 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3364 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3366 if (dragInfo.pos.x >= 0) {
\r
3367 x = dragInfo.pos.x - squareSize / 2;
\r
3368 y = dragInfo.pos.y - squareSize / 2;
\r
3369 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3371 if (dragInfo.lastpos.x >= 0) {
\r
3372 x = dragInfo.lastpos.x - squareSize / 2;
\r
3373 y = dragInfo.lastpos.y - squareSize / 2;
\r
3374 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3378 /* Are we animating a move?
\r
3380 * - remove the piece from the board (temporarely)
\r
3381 * - calculate the clipping region
\r
3383 if (!fullrepaint) {
\r
3384 if (animInfo.piece != EmptySquare) {
\r
3385 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3386 x = boardRect.left + animInfo.lastpos.x;
\r
3387 y = boardRect.top + animInfo.lastpos.y;
\r
3388 x2 = boardRect.left + animInfo.pos.x;
\r
3389 y2 = boardRect.top + animInfo.pos.y;
\r
3390 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3391 /* Slight kludge. The real problem is that after AnimateMove is
\r
3392 done, the position on the screen does not match lastDrawn.
\r
3393 This currently causes trouble only on e.p. captures in
\r
3394 atomic, where the piece moves to an empty square and then
\r
3395 explodes. The old and new positions both had an empty square
\r
3396 at the destination, but animation has drawn a piece there and
\r
3397 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3398 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3402 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3403 if (num_clips == 0)
\r
3404 fullrepaint = TRUE;
\r
3406 /* Set clipping on the memory DC */
\r
3407 if (!fullrepaint) {
\r
3408 SelectClipRgn(hdcmem, clips[0]);
\r
3409 for (x = 1; x < num_clips; x++) {
\r
3410 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3411 abort(); // this should never ever happen!
\r
3415 /* Do all the drawing to the memory DC */
\r
3416 if(explodeInfo.radius) { // [HGM] atomic
\r
3418 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3419 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3420 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3421 x += squareSize/2;
\r
3422 y += squareSize/2;
\r
3423 if(!fullrepaint) {
\r
3424 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3425 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3427 DrawGridOnDC(hdcmem);
\r
3428 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3429 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3430 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3431 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3432 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3433 SelectObject(hdcmem, oldBrush);
\r
3435 DrawGridOnDC(hdcmem);
\r
3436 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3437 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3438 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3440 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3441 oldPartnerHighlight = partnerHighlightInfo;
\r
3443 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3445 if(nr == 0) // [HGM] dual: markers only on left board
\r
3446 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3447 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3448 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3449 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3450 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3451 SquareToPos(row, column, &x, &y);
\r
3452 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3453 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3454 SelectObject(hdcmem, oldBrush);
\r
3459 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3460 if(appData.autoLogo) {
\r
3462 switch(gameMode) { // pick logos based on game mode
\r
3463 case IcsObserving:
\r
3464 whiteLogo = second.programLogo; // ICS logo
\r
3465 blackLogo = second.programLogo;
\r
3468 case IcsPlayingWhite:
\r
3469 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3470 blackLogo = second.programLogo; // ICS logo
\r
3472 case IcsPlayingBlack:
\r
3473 whiteLogo = second.programLogo; // ICS logo
\r
3474 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3476 case TwoMachinesPlay:
\r
3477 if(first.twoMachinesColor[0] == 'b') {
\r
3478 whiteLogo = second.programLogo;
\r
3479 blackLogo = first.programLogo;
\r
3482 case MachinePlaysWhite:
\r
3483 blackLogo = userLogo;
\r
3485 case MachinePlaysBlack:
\r
3486 whiteLogo = userLogo;
\r
3487 blackLogo = first.programLogo;
\r
3490 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3491 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3494 if( appData.highlightMoveWithArrow ) {
\r
3495 DrawArrowHighlight(hdcmem);
\r
3498 DrawCoordsOnDC(hdcmem);
\r
3500 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3501 /* to make sure lastDrawn contains what is actually drawn */
\r
3503 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3504 if (dragged_piece != EmptySquare) {
\r
3505 /* [HGM] or restack */
\r
3506 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3507 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3509 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3510 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3511 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3512 x = dragInfo.pos.x - squareSize / 2;
\r
3513 y = dragInfo.pos.y - squareSize / 2;
\r
3514 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3515 ((int) dragged_piece < (int) BlackPawn),
\r
3516 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3519 /* Put the animated piece back into place and draw it */
\r
3520 if (animInfo.piece != EmptySquare) {
\r
3521 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3522 x = boardRect.left + animInfo.pos.x;
\r
3523 y = boardRect.top + animInfo.pos.y;
\r
3524 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3525 ((int) animInfo.piece < (int) BlackPawn),
\r
3526 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3529 /* Release the bufferBitmap by selecting in the old bitmap
\r
3530 * and delete the memory DC
\r
3532 SelectObject(hdcmem, oldBitmap);
\r
3535 /* Set clipping on the target DC */
\r
3536 if (!fullrepaint) {
\r
3537 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3539 GetRgnBox(clips[x], &rect);
\r
3540 DeleteObject(clips[x]);
\r
3541 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3542 rect.right + wpMain.width/2, rect.bottom);
\r
3544 SelectClipRgn(hdc, clips[0]);
\r
3545 for (x = 1; x < num_clips; x++) {
\r
3546 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3547 abort(); // this should never ever happen!
\r
3551 /* Copy the new bitmap onto the screen in one go.
\r
3552 * This way we avoid any flickering
\r
3554 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3555 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3556 boardRect.right - boardRect.left,
\r
3557 boardRect.bottom - boardRect.top,
\r
3558 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3559 if(saveDiagFlag) {
\r
3560 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3561 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3563 GetObject(bufferBitmap, sizeof(b), &b);
\r
3564 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3565 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3566 bih.biWidth = b.bmWidth;
\r
3567 bih.biHeight = b.bmHeight;
\r
3569 bih.biBitCount = b.bmBitsPixel;
\r
3570 bih.biCompression = 0;
\r
3571 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3572 bih.biXPelsPerMeter = 0;
\r
3573 bih.biYPelsPerMeter = 0;
\r
3574 bih.biClrUsed = 0;
\r
3575 bih.biClrImportant = 0;
\r
3576 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3577 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3578 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3579 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3581 wb = b.bmWidthBytes;
\r
3583 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3584 int k = ((int*) pData)[i];
\r
3585 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3586 if(j >= 16) break;
\r
3588 if(j >= nrColors) nrColors = j+1;
\r
3590 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3592 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3593 for(w=0; w<(wb>>2); w+=2) {
\r
3594 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3595 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3596 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3597 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3598 pData[p++] = m | j<<4;
\r
3600 while(p&3) pData[p++] = 0;
\r
3603 wb = ((wb+31)>>5)<<2;
\r
3605 // write BITMAPFILEHEADER
\r
3606 fprintf(diagFile, "BM");
\r
3607 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3608 fputDW(diagFile, 0);
\r
3609 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3610 // write BITMAPINFOHEADER
\r
3611 fputDW(diagFile, 40);
\r
3612 fputDW(diagFile, b.bmWidth);
\r
3613 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3614 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3615 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3616 fputDW(diagFile, 0);
\r
3617 fputDW(diagFile, 0);
\r
3618 fputDW(diagFile, 0);
\r
3619 fputDW(diagFile, 0);
\r
3620 fputDW(diagFile, 0);
\r
3621 fputDW(diagFile, 0);
\r
3622 // write color table
\r
3624 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3625 // write bitmap data
\r
3626 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3627 fputc(pData[i], diagFile);
\r
3631 SelectObject(tmphdc, oldBitmap);
\r
3633 /* Massive cleanup */
\r
3634 for (x = 0; x < num_clips; x++)
\r
3635 DeleteObject(clips[x]);
\r
3638 DeleteObject(bufferBitmap);
\r
3641 ReleaseDC(hwndMain, hdc);
\r
3643 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3645 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3647 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3650 /* CopyBoard(lastDrawn, board);*/
\r
3651 lastDrawnHighlight = highlightInfo;
\r
3652 lastDrawnPremove = premoveHighlightInfo;
\r
3653 lastDrawnFlipView = flipView;
\r
3654 lastDrawnValid[nr] = 1;
\r
3657 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3662 saveDiagFlag = 1; diagFile = f;
\r
3663 HDCDrawPosition(NULL, TRUE, NULL);
\r
3667 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3674 /*---------------------------------------------------------------------------*\
\r
3675 | CLIENT PAINT PROCEDURE
\r
3676 | This is the main event-handler for the WM_PAINT message.
\r
3678 \*---------------------------------------------------------------------------*/
\r
3680 PaintProc(HWND hwnd)
\r
3686 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3687 if (IsIconic(hwnd)) {
\r
3688 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3690 if (!appData.monoMode) {
\r
3691 SelectPalette(hdc, hPal, FALSE);
\r
3692 RealizePalette(hdc);
\r
3694 HDCDrawPosition(hdc, 1, NULL);
\r
3695 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3696 flipView = !flipView; partnerUp = !partnerUp;
\r
3697 HDCDrawPosition(hdc, 1, NULL);
\r
3698 flipView = !flipView; partnerUp = !partnerUp;
\r
3701 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3702 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3703 ETO_CLIPPED|ETO_OPAQUE,
\r
3704 &messageRect, messageText, strlen(messageText), NULL);
\r
3705 SelectObject(hdc, oldFont);
\r
3706 DisplayBothClocks();
\r
3708 EndPaint(hwnd,&ps);
\r
3716 * If the user selects on a border boundary, return -1; if off the board,
\r
3717 * return -2. Otherwise map the event coordinate to the square.
\r
3718 * The offset boardRect.left or boardRect.top must already have been
\r
3719 * subtracted from x.
\r
3721 int EventToSquare(x, limit)
\r
3729 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3731 x /= (squareSize + lineGap);
\r
3743 DropEnable dropEnables[] = {
\r
3744 { 'P', DP_Pawn, "Pawn" },
\r
3745 { 'N', DP_Knight, "Knight" },
\r
3746 { 'B', DP_Bishop, "Bishop" },
\r
3747 { 'R', DP_Rook, "Rook" },
\r
3748 { 'Q', DP_Queen, "Queen" },
\r
3752 SetupDropMenu(HMENU hmenu)
\r
3754 int i, count, enable;
\r
3756 extern char white_holding[], black_holding[];
\r
3757 char item[MSG_SIZ];
\r
3759 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3760 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3761 dropEnables[i].piece);
\r
3763 while (p && *p++ == dropEnables[i].piece) count++;
\r
3764 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3765 enable = count > 0 || !appData.testLegality
\r
3766 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3767 && !appData.icsActive);
\r
3768 ModifyMenu(hmenu, dropEnables[i].command,
\r
3769 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3770 dropEnables[i].command, item);
\r
3774 void DragPieceBegin(int x, int y)
\r
3776 dragInfo.lastpos.x = boardRect.left + x;
\r
3777 dragInfo.lastpos.y = boardRect.top + y;
\r
3778 dragInfo.from.x = fromX;
\r
3779 dragInfo.from.y = fromY;
\r
3780 dragInfo.start = dragInfo.from;
\r
3781 SetCapture(hwndMain);
\r
3784 void DragPieceEnd(int x, int y)
\r
3787 dragInfo.start.x = dragInfo.start.y = -1;
\r
3788 dragInfo.from = dragInfo.start;
\r
3789 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3792 /* Event handler for mouse messages */
\r
3794 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3798 static int recursive = 0;
\r
3800 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3803 if (message == WM_MBUTTONUP) {
\r
3804 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3805 to the middle button: we simulate pressing the left button too!
\r
3807 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3808 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3814 pt.x = LOWORD(lParam);
\r
3815 pt.y = HIWORD(lParam);
\r
3816 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3817 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3818 if (!flipView && y >= 0) {
\r
3819 y = BOARD_HEIGHT - 1 - y;
\r
3821 if (flipView && x >= 0) {
\r
3822 x = BOARD_WIDTH - 1 - x;
\r
3825 switch (message) {
\r
3826 case WM_LBUTTONDOWN:
\r
3827 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3828 if (gameMode == EditPosition) {
\r
3829 SetWhiteToPlayEvent();
\r
3830 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3831 AdjustClock(flipClock, -1);
\r
3832 } else if (gameMode == IcsPlayingBlack ||
\r
3833 gameMode == MachinePlaysWhite) {
\r
3836 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3837 if (gameMode == EditPosition) {
\r
3838 SetBlackToPlayEvent();
\r
3839 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3840 AdjustClock(!flipClock, -1);
\r
3841 } else if (gameMode == IcsPlayingWhite ||
\r
3842 gameMode == MachinePlaysBlack) {
\r
3846 dragInfo.start.x = dragInfo.start.y = -1;
\r
3847 dragInfo.from = dragInfo.start;
\r
3848 if(fromX == -1 && frozen) { // not sure where this is for
\r
3849 fromX = fromY = -1;
\r
3850 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3853 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3854 DrawPosition(TRUE, NULL);
\r
3857 case WM_LBUTTONUP:
\r
3858 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3859 DrawPosition(TRUE, NULL);
\r
3862 case WM_MOUSEMOVE:
\r
3863 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3864 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3865 if ((appData.animateDragging || appData.highlightDragging)
\r
3866 && (wParam & MK_LBUTTON)
\r
3867 && dragInfo.from.x >= 0)
\r
3869 BOOL full_repaint = FALSE;
\r
3871 if (appData.animateDragging) {
\r
3872 dragInfo.pos = pt;
\r
3874 if (appData.highlightDragging) {
\r
3875 SetHighlights(fromX, fromY, x, y);
\r
3876 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3877 full_repaint = TRUE;
\r
3881 DrawPosition( full_repaint, NULL);
\r
3883 dragInfo.lastpos = dragInfo.pos;
\r
3887 case WM_MOUSEWHEEL: // [DM]
\r
3888 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3889 /* Mouse Wheel is being rolled forward
\r
3890 * Play moves forward
\r
3892 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
3893 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3894 /* Mouse Wheel is being rolled backward
\r
3895 * Play moves backward
\r
3897 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
3898 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3902 case WM_MBUTTONUP:
\r
3903 case WM_RBUTTONUP:
\r
3905 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3908 case WM_MBUTTONDOWN:
\r
3909 case WM_RBUTTONDOWN:
\r
3912 fromX = fromY = -1;
\r
3913 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3914 dragInfo.start.x = dragInfo.start.y = -1;
\r
3915 dragInfo.from = dragInfo.start;
\r
3916 dragInfo.lastpos = dragInfo.pos;
\r
3917 if (appData.highlightDragging) {
\r
3918 ClearHighlights();
\r
3921 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3922 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3923 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
3924 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3925 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
3929 DrawPosition(TRUE, NULL);
\r
3931 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3934 if (message == WM_MBUTTONDOWN) {
\r
3935 buttonCount = 3; /* even if system didn't think so */
\r
3936 if (wParam & MK_SHIFT)
\r
3937 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3939 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3940 } else { /* message == WM_RBUTTONDOWN */
\r
3941 /* Just have one menu, on the right button. Windows users don't
\r
3942 think to try the middle one, and sometimes other software steals
\r
3943 it, or it doesn't really exist. */
\r
3944 if(gameInfo.variant != VariantShogi)
\r
3945 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3947 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3951 SetCapture(hwndMain);
3954 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3955 SetupDropMenu(hmenu);
\r
3956 MenuPopup(hwnd, pt, hmenu, -1);
\r
3966 /* Preprocess messages for buttons in main window */
\r
3968 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3970 int id = GetWindowLong(hwnd, GWL_ID);
\r
3973 for (i=0; i<N_BUTTONS; i++) {
\r
3974 if (buttonDesc[i].id == id) break;
\r
3976 if (i == N_BUTTONS) return 0;
\r
3977 switch (message) {
\r
3982 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3983 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3990 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3993 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3994 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3995 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3996 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3998 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4000 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4001 PopUpMoveDialog((char)wParam);
\r
4007 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4010 /* Process messages for Promotion dialog box */
\r
4012 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4016 switch (message) {
\r
4017 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4018 /* Center the dialog over the application window */
\r
4019 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4020 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4021 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4022 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4023 SW_SHOW : SW_HIDE);
\r
4024 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4025 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4026 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4027 PieceToChar(WhiteAngel) != '~') ||
\r
4028 (PieceToChar(BlackAngel) >= 'A' &&
\r
4029 PieceToChar(BlackAngel) != '~') ) ?
\r
4030 SW_SHOW : SW_HIDE);
\r
4031 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4032 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4033 PieceToChar(WhiteMarshall) != '~') ||
\r
4034 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4035 PieceToChar(BlackMarshall) != '~') ) ?
\r
4036 SW_SHOW : SW_HIDE);
\r
4037 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4038 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4039 gameInfo.variant != VariantShogi ?
\r
4040 SW_SHOW : SW_HIDE);
\r
4041 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4042 gameInfo.variant != VariantShogi ?
\r
4043 SW_SHOW : SW_HIDE);
\r
4044 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4045 gameInfo.variant == VariantShogi ?
\r
4046 SW_SHOW : SW_HIDE);
\r
4047 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4048 gameInfo.variant == VariantShogi ?
\r
4049 SW_SHOW : SW_HIDE);
\r
4050 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4051 gameInfo.variant == VariantSuper ?
\r
4052 SW_SHOW : SW_HIDE);
\r
4055 case WM_COMMAND: /* message: received a command */
\r
4056 switch (LOWORD(wParam)) {
\r
4058 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4059 ClearHighlights();
\r
4060 DrawPosition(FALSE, NULL);
\r
4063 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4066 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4069 promoChar = PieceToChar(BlackRook);
\r
4072 promoChar = PieceToChar(BlackBishop);
\r
4074 case PB_Chancellor:
\r
4075 promoChar = PieceToChar(BlackMarshall);
\r
4077 case PB_Archbishop:
\r
4078 promoChar = PieceToChar(BlackAngel);
\r
4081 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4086 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4087 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4088 only show the popup when we are already sure the move is valid or
\r
4089 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4090 will figure out it is a promotion from the promoChar. */
\r
4091 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4092 fromX = fromY = -1;
\r
4093 if (!appData.highlightLastMove) {
\r
4094 ClearHighlights();
\r
4095 DrawPosition(FALSE, NULL);
\r
4102 /* Pop up promotion dialog */
\r
4104 PromotionPopup(HWND hwnd)
\r
4108 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4109 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4110 hwnd, (DLGPROC)lpProc);
\r
4111 FreeProcInstance(lpProc);
\r
4117 DrawPosition(TRUE, NULL);
\r
4118 PromotionPopup(hwndMain);
\r
4121 /* Toggle ShowThinking */
\r
4123 ToggleShowThinking()
\r
4125 appData.showThinking = !appData.showThinking;
\r
4126 ShowThinkingEvent();
\r
4130 LoadGameDialog(HWND hwnd, char* title)
\r
4134 char fileTitle[MSG_SIZ];
\r
4135 f = OpenFileDialog(hwnd, "rb", "",
\r
4136 appData.oldSaveStyle ? "gam" : "pgn",
\r
4138 title, &number, fileTitle, NULL);
\r
4140 cmailMsgLoaded = FALSE;
\r
4141 if (number == 0) {
\r
4142 int error = GameListBuild(f);
\r
4144 DisplayError("Cannot build game list", error);
\r
4145 } else if (!ListEmpty(&gameList) &&
\r
4146 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4147 GameListPopUp(f, fileTitle);
\r
4150 GameListDestroy();
\r
4153 LoadGame(f, number, fileTitle, FALSE);
\r
4157 int get_term_width()
\r
4162 HFONT hfont, hold_font;
\r
4167 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4171 // get the text metrics
\r
4172 hdc = GetDC(hText);
\r
4173 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4174 if (consoleCF.dwEffects & CFE_BOLD)
\r
4175 lf.lfWeight = FW_BOLD;
\r
4176 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4177 lf.lfItalic = TRUE;
\r
4178 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4179 lf.lfStrikeOut = TRUE;
\r
4180 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4181 lf.lfUnderline = TRUE;
\r
4182 hfont = CreateFontIndirect(&lf);
\r
4183 hold_font = SelectObject(hdc, hfont);
\r
4184 GetTextMetrics(hdc, &tm);
\r
4185 SelectObject(hdc, hold_font);
\r
4186 DeleteObject(hfont);
\r
4187 ReleaseDC(hText, hdc);
\r
4189 // get the rectangle
\r
4190 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4192 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4195 void UpdateICSWidth(HWND hText)
\r
4197 LONG old_width, new_width;
\r
4199 new_width = get_term_width(hText, FALSE);
\r
4200 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4201 if (new_width != old_width)
\r
4203 ics_update_width(new_width);
\r
4204 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4209 ChangedConsoleFont()
\r
4212 CHARRANGE tmpsel, sel;
\r
4213 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4214 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4215 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4218 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4219 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4220 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4221 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4222 * size. This was undocumented in the version of MSVC++ that I had
\r
4223 * when I wrote the code, but is apparently documented now.
\r
4225 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4226 cfmt.bCharSet = f->lf.lfCharSet;
\r
4227 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4228 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4229 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4230 /* Why are the following seemingly needed too? */
\r
4231 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4232 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4233 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4235 tmpsel.cpMax = -1; /*999999?*/
\r
4236 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4237 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4238 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4239 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4241 paraf.cbSize = sizeof(paraf);
\r
4242 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4243 paraf.dxStartIndent = 0;
\r
4244 paraf.dxOffset = WRAP_INDENT;
\r
4245 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4246 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4247 UpdateICSWidth(hText);
\r
4250 /*---------------------------------------------------------------------------*\
\r
4252 * Window Proc for main window
\r
4254 \*---------------------------------------------------------------------------*/
\r
4256 /* Process messages for main window, etc. */
\r
4258 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4261 int wmId, wmEvent;
\r
4265 char fileTitle[MSG_SIZ];
\r
4266 char buf[MSG_SIZ];
\r
4267 static SnapData sd;
\r
4269 switch (message) {
\r
4271 case WM_PAINT: /* message: repaint portion of window */
\r
4275 case WM_ERASEBKGND:
\r
4276 if (IsIconic(hwnd)) {
\r
4277 /* Cheat; change the message */
\r
4278 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4280 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4284 case WM_LBUTTONDOWN:
\r
4285 case WM_MBUTTONDOWN:
\r
4286 case WM_RBUTTONDOWN:
\r
4287 case WM_LBUTTONUP:
\r
4288 case WM_MBUTTONUP:
\r
4289 case WM_RBUTTONUP:
\r
4290 case WM_MOUSEMOVE:
\r
4291 case WM_MOUSEWHEEL:
\r
4292 MouseEvent(hwnd, message, wParam, lParam);
\r
4295 JAWS_KB_NAVIGATION
\r
4299 JAWS_ALT_INTERCEPT
\r
4301 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4302 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4303 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4304 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4306 SendMessage(h, message, wParam, lParam);
\r
4307 } else if(lParam != KF_REPEAT) {
\r
4308 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4309 PopUpMoveDialog((char)wParam);
\r
4310 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4311 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4316 case WM_PALETTECHANGED:
\r
4317 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4319 HDC hdc = GetDC(hwndMain);
\r
4320 SelectPalette(hdc, hPal, TRUE);
\r
4321 nnew = RealizePalette(hdc);
\r
4323 paletteChanged = TRUE;
\r
4324 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4326 ReleaseDC(hwnd, hdc);
\r
4330 case WM_QUERYNEWPALETTE:
\r
4331 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4333 HDC hdc = GetDC(hwndMain);
\r
4334 paletteChanged = FALSE;
\r
4335 SelectPalette(hdc, hPal, FALSE);
\r
4336 nnew = RealizePalette(hdc);
\r
4338 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4340 ReleaseDC(hwnd, hdc);
\r
4345 case WM_COMMAND: /* message: command from application menu */
\r
4346 wmId = LOWORD(wParam);
\r
4347 wmEvent = HIWORD(wParam);
\r
4352 SAY("new game enter a move to play against the computer with white");
\r
4355 case IDM_NewGameFRC:
\r
4356 if( NewGameFRC() == 0 ) {
\r
4361 case IDM_NewVariant:
\r
4362 NewVariantPopup(hwnd);
\r
4365 case IDM_LoadGame:
\r
4366 LoadGameDialog(hwnd, "Load Game from File");
\r
4369 case IDM_LoadNextGame:
\r
4373 case IDM_LoadPrevGame:
\r
4377 case IDM_ReloadGame:
\r
4381 case IDM_LoadPosition:
\r
4382 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4383 Reset(FALSE, TRUE);
\r
4386 f = OpenFileDialog(hwnd, "rb", "",
\r
4387 appData.oldSaveStyle ? "pos" : "fen",
\r
4389 "Load Position from File", &number, fileTitle, NULL);
\r
4391 LoadPosition(f, number, fileTitle);
\r
4395 case IDM_LoadNextPosition:
\r
4396 ReloadPosition(1);
\r
4399 case IDM_LoadPrevPosition:
\r
4400 ReloadPosition(-1);
\r
4403 case IDM_ReloadPosition:
\r
4404 ReloadPosition(0);
\r
4407 case IDM_SaveGame:
\r
4408 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4409 f = OpenFileDialog(hwnd, "a", defName,
\r
4410 appData.oldSaveStyle ? "gam" : "pgn",
\r
4412 "Save Game to File", NULL, fileTitle, NULL);
\r
4414 SaveGame(f, 0, "");
\r
4418 case IDM_SavePosition:
\r
4419 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4420 f = OpenFileDialog(hwnd, "a", defName,
\r
4421 appData.oldSaveStyle ? "pos" : "fen",
\r
4423 "Save Position to File", NULL, fileTitle, NULL);
\r
4425 SavePosition(f, 0, "");
\r
4429 case IDM_SaveDiagram:
\r
4430 defName = "diagram";
\r
4431 f = OpenFileDialog(hwnd, "wb", defName,
\r
4434 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4440 case IDM_CopyGame:
\r
4441 CopyGameToClipboard();
\r
4444 case IDM_PasteGame:
\r
4445 PasteGameFromClipboard();
\r
4448 case IDM_CopyGameListToClipboard:
\r
4449 CopyGameListToClipboard();
\r
4452 /* [AS] Autodetect FEN or PGN data */
\r
4453 case IDM_PasteAny:
\r
4454 PasteGameOrFENFromClipboard();
\r
4457 /* [AS] Move history */
\r
4458 case IDM_ShowMoveHistory:
\r
4459 if( MoveHistoryIsUp() ) {
\r
4460 MoveHistoryPopDown();
\r
4463 MoveHistoryPopUp();
\r
4467 /* [AS] Eval graph */
\r
4468 case IDM_ShowEvalGraph:
\r
4469 if( EvalGraphIsUp() ) {
\r
4470 EvalGraphPopDown();
\r
4474 SetFocus(hwndMain);
\r
4478 /* [AS] Engine output */
\r
4479 case IDM_ShowEngineOutput:
\r
4480 if( EngineOutputIsUp() ) {
\r
4481 EngineOutputPopDown();
\r
4484 EngineOutputPopUp();
\r
4488 /* [AS] User adjudication */
\r
4489 case IDM_UserAdjudication_White:
\r
4490 UserAdjudicationEvent( +1 );
\r
4493 case IDM_UserAdjudication_Black:
\r
4494 UserAdjudicationEvent( -1 );
\r
4497 case IDM_UserAdjudication_Draw:
\r
4498 UserAdjudicationEvent( 0 );
\r
4501 /* [AS] Game list options dialog */
\r
4502 case IDM_GameListOptions:
\r
4503 GameListOptions();
\r
4510 case IDM_CopyPosition:
\r
4511 CopyFENToClipboard();
\r
4514 case IDM_PastePosition:
\r
4515 PasteFENFromClipboard();
\r
4518 case IDM_MailMove:
\r
4522 case IDM_ReloadCMailMsg:
\r
4523 Reset(TRUE, TRUE);
\r
4524 ReloadCmailMsgEvent(FALSE);
\r
4527 case IDM_Minimize:
\r
4528 ShowWindow(hwnd, SW_MINIMIZE);
\r
4535 case IDM_MachineWhite:
\r
4536 MachineWhiteEvent();
\r
4538 * refresh the tags dialog only if it's visible
\r
4540 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4542 tags = PGNTags(&gameInfo);
\r
4543 TagsPopUp(tags, CmailMsg());
\r
4546 SAY("computer starts playing white");
\r
4549 case IDM_MachineBlack:
\r
4550 MachineBlackEvent();
\r
4552 * refresh the tags dialog only if it's visible
\r
4554 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4556 tags = PGNTags(&gameInfo);
\r
4557 TagsPopUp(tags, CmailMsg());
\r
4560 SAY("computer starts playing black");
\r
4563 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4564 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4565 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4566 appData.matchGames = appData.defaultMatchGames;
\r
4569 case IDM_TwoMachines:
\r
4570 TwoMachinesEvent();
\r
4572 * refresh the tags dialog only if it's visible
\r
4574 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4576 tags = PGNTags(&gameInfo);
\r
4577 TagsPopUp(tags, CmailMsg());
\r
4580 SAY("computer starts playing both sides");
\r
4583 case IDM_AnalysisMode:
\r
4584 if (!first.analysisSupport) {
\r
4585 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4586 DisplayError(buf, 0);
\r
4588 SAY("analyzing current position");
\r
4589 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4590 if (appData.icsActive) {
\r
4591 if (gameMode != IcsObserving) {
\r
4592 sprintf(buf, "You are not observing a game");
\r
4593 DisplayError(buf, 0);
\r
4594 /* secure check */
\r
4595 if (appData.icsEngineAnalyze) {
\r
4596 if (appData.debugMode)
\r
4597 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4598 ExitAnalyzeMode();
\r
4604 /* if enable, user want disable icsEngineAnalyze */
\r
4605 if (appData.icsEngineAnalyze) {
\r
4606 ExitAnalyzeMode();
\r
4610 appData.icsEngineAnalyze = TRUE;
\r
4611 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4614 if (!appData.showThinking) ToggleShowThinking();
\r
4615 AnalyzeModeEvent();
\r
4619 case IDM_AnalyzeFile:
\r
4620 if (!first.analysisSupport) {
\r
4621 char buf[MSG_SIZ];
\r
4622 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4623 DisplayError(buf, 0);
\r
4625 if (!appData.showThinking) ToggleShowThinking();
\r
4626 AnalyzeFileEvent();
\r
4627 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4628 AnalysisPeriodicEvent(1);
\r
4632 case IDM_IcsClient:
\r
4636 case IDM_EditGame:
\r
4641 case IDM_EditPosition:
\r
4642 EditPositionEvent();
\r
4643 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4646 case IDM_Training:
\r
4650 case IDM_ShowGameList:
\r
4651 ShowGameListProc();
\r
4654 case IDM_EditTags:
\r
4658 case IDM_EditComment:
\r
4659 if (commentUp && editComment) {
\r
4662 EditCommentEvent();
\r
4682 case IDM_CallFlag:
\r
4702 case IDM_StopObserving:
\r
4703 StopObservingEvent();
\r
4706 case IDM_StopExamining:
\r
4707 StopExaminingEvent();
\r
4711 UploadGameEvent();
\r
4714 case IDM_TypeInMove:
\r
4715 PopUpMoveDialog('\000');
\r
4718 case IDM_TypeInName:
\r
4719 PopUpNameDialog('\000');
\r
4722 case IDM_Backward:
\r
4724 SetFocus(hwndMain);
\r
4731 SetFocus(hwndMain);
\r
4736 SetFocus(hwndMain);
\r
4741 SetFocus(hwndMain);
\r
4745 RevertEvent(FALSE);
\r
4748 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4749 RevertEvent(TRUE);
\r
4752 case IDM_TruncateGame:
\r
4753 TruncateGameEvent();
\r
4760 case IDM_RetractMove:
\r
4761 RetractMoveEvent();
\r
4764 case IDM_FlipView:
\r
4765 flipView = !flipView;
\r
4766 DrawPosition(FALSE, NULL);
\r
4769 case IDM_FlipClock:
\r
4770 flipClock = !flipClock;
\r
4771 DisplayBothClocks();
\r
4772 DrawPosition(FALSE, NULL);
\r
4775 case IDM_MuteSounds:
\r
4776 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4777 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4778 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4781 case IDM_GeneralOptions:
\r
4782 GeneralOptionsPopup(hwnd);
\r
4783 DrawPosition(TRUE, NULL);
\r
4786 case IDM_BoardOptions:
\r
4787 BoardOptionsPopup(hwnd);
\r
4790 case IDM_EnginePlayOptions:
\r
4791 EnginePlayOptionsPopup(hwnd);
\r
4794 case IDM_Engine1Options:
\r
4795 EngineOptionsPopup(hwnd, &first);
\r
4798 case IDM_Engine2Options:
\r
4799 EngineOptionsPopup(hwnd, &second);
\r
4802 case IDM_OptionsUCI:
\r
4803 UciOptionsPopup(hwnd);
\r
4806 case IDM_IcsOptions:
\r
4807 IcsOptionsPopup(hwnd);
\r
4811 FontsOptionsPopup(hwnd);
\r
4815 SoundOptionsPopup(hwnd);
\r
4818 case IDM_CommPort:
\r
4819 CommPortOptionsPopup(hwnd);
\r
4822 case IDM_LoadOptions:
\r
4823 LoadOptionsPopup(hwnd);
\r
4826 case IDM_SaveOptions:
\r
4827 SaveOptionsPopup(hwnd);
\r
4830 case IDM_TimeControl:
\r
4831 TimeControlOptionsPopup(hwnd);
\r
4834 case IDM_SaveSettings:
\r
4835 SaveSettings(settingsFileName);
\r
4838 case IDM_SaveSettingsOnExit:
\r
4839 saveSettingsOnExit = !saveSettingsOnExit;
\r
4840 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4841 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4842 MF_CHECKED : MF_UNCHECKED));
\r
4853 case IDM_AboutGame:
\r
4858 appData.debugMode = !appData.debugMode;
\r
4859 if (appData.debugMode) {
\r
4860 char dir[MSG_SIZ];
\r
4861 GetCurrentDirectory(MSG_SIZ, dir);
\r
4862 SetCurrentDirectory(installDir);
\r
4863 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4864 SetCurrentDirectory(dir);
\r
4865 setbuf(debugFP, NULL);
\r
4872 case IDM_HELPCONTENTS:
\r
4873 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4874 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4875 MessageBox (GetFocus(),
\r
4876 "Unable to activate help",
\r
4877 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4881 case IDM_HELPSEARCH:
\r
4882 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4883 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4884 MessageBox (GetFocus(),
\r
4885 "Unable to activate help",
\r
4886 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4890 case IDM_HELPHELP:
\r
4891 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4892 MessageBox (GetFocus(),
\r
4893 "Unable to activate help",
\r
4894 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4899 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4901 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4902 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4903 FreeProcInstance(lpProc);
\r
4906 case IDM_DirectCommand1:
\r
4907 AskQuestionEvent("Direct Command",
\r
4908 "Send to chess program:", "", "1");
\r
4910 case IDM_DirectCommand2:
\r
4911 AskQuestionEvent("Direct Command",
\r
4912 "Send to second chess program:", "", "2");
\r
4915 case EP_WhitePawn:
\r
4916 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4917 fromX = fromY = -1;
\r
4920 case EP_WhiteKnight:
\r
4921 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4922 fromX = fromY = -1;
\r
4925 case EP_WhiteBishop:
\r
4926 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4927 fromX = fromY = -1;
\r
4930 case EP_WhiteRook:
\r
4931 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4932 fromX = fromY = -1;
\r
4935 case EP_WhiteQueen:
\r
4936 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4937 fromX = fromY = -1;
\r
4940 case EP_WhiteFerz:
\r
4941 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4942 fromX = fromY = -1;
\r
4945 case EP_WhiteWazir:
\r
4946 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4947 fromX = fromY = -1;
\r
4950 case EP_WhiteAlfil:
\r
4951 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4952 fromX = fromY = -1;
\r
4955 case EP_WhiteCannon:
\r
4956 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4957 fromX = fromY = -1;
\r
4960 case EP_WhiteCardinal:
\r
4961 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4962 fromX = fromY = -1;
\r
4965 case EP_WhiteMarshall:
\r
4966 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4967 fromX = fromY = -1;
\r
4970 case EP_WhiteKing:
\r
4971 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4972 fromX = fromY = -1;
\r
4975 case EP_BlackPawn:
\r
4976 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4977 fromX = fromY = -1;
\r
4980 case EP_BlackKnight:
\r
4981 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4982 fromX = fromY = -1;
\r
4985 case EP_BlackBishop:
\r
4986 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4987 fromX = fromY = -1;
\r
4990 case EP_BlackRook:
\r
4991 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4992 fromX = fromY = -1;
\r
4995 case EP_BlackQueen:
\r
4996 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4997 fromX = fromY = -1;
\r
5000 case EP_BlackFerz:
\r
5001 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5002 fromX = fromY = -1;
\r
5005 case EP_BlackWazir:
\r
5006 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5007 fromX = fromY = -1;
\r
5010 case EP_BlackAlfil:
\r
5011 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5012 fromX = fromY = -1;
\r
5015 case EP_BlackCannon:
\r
5016 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5017 fromX = fromY = -1;
\r
5020 case EP_BlackCardinal:
\r
5021 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5022 fromX = fromY = -1;
\r
5025 case EP_BlackMarshall:
\r
5026 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5027 fromX = fromY = -1;
\r
5030 case EP_BlackKing:
\r
5031 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5032 fromX = fromY = -1;
\r
5035 case EP_EmptySquare:
\r
5036 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5037 fromX = fromY = -1;
\r
5040 case EP_ClearBoard:
\r
5041 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5042 fromX = fromY = -1;
\r
5046 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5047 fromX = fromY = -1;
\r
5051 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5052 fromX = fromY = -1;
\r
5056 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5057 fromX = fromY = -1;
\r
5061 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5062 fromX = fromY = -1;
\r
5066 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5067 fromX = fromY = -1;
\r
5071 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5072 fromX = fromY = -1;
\r
5076 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5077 fromX = fromY = -1;
\r
5081 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5082 fromX = fromY = -1;
\r
5086 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5087 fromX = fromY = -1;
\r
5091 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5097 case CLOCK_TIMER_ID:
\r
5098 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5099 clockTimerEvent = 0;
\r
5100 DecrementClocks(); /* call into back end */
\r
5102 case LOAD_GAME_TIMER_ID:
\r
5103 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5104 loadGameTimerEvent = 0;
\r
5105 AutoPlayGameLoop(); /* call into back end */
\r
5107 case ANALYSIS_TIMER_ID:
\r
5108 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5109 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5110 AnalysisPeriodicEvent(0);
\r
5112 KillTimer(hwnd, analysisTimerEvent);
\r
5113 analysisTimerEvent = 0;
\r
5116 case DELAYED_TIMER_ID:
\r
5117 KillTimer(hwnd, delayedTimerEvent);
\r
5118 delayedTimerEvent = 0;
\r
5119 delayedTimerCallback();
\r
5124 case WM_USER_Input:
\r
5125 InputEvent(hwnd, message, wParam, lParam);
\r
5128 /* [AS] Also move "attached" child windows */
\r
5129 case WM_WINDOWPOSCHANGING:
\r
5131 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5132 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5134 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5135 /* Window is moving */
\r
5138 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5139 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5140 rcMain.right = wpMain.x + wpMain.width;
\r
5141 rcMain.top = wpMain.y;
\r
5142 rcMain.bottom = wpMain.y + wpMain.height;
\r
5144 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5145 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5146 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5147 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5148 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5149 wpMain.x = lpwp->x;
\r
5150 wpMain.y = lpwp->y;
\r
5155 /* [AS] Snapping */
\r
5156 case WM_ENTERSIZEMOVE:
\r
5157 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5158 if (hwnd == hwndMain) {
\r
5159 doingSizing = TRUE;
\r
5162 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5166 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5167 if (hwnd == hwndMain) {
\r
5168 lastSizing = wParam;
\r
5173 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5174 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5176 case WM_EXITSIZEMOVE:
\r
5177 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5178 if (hwnd == hwndMain) {
\r
5180 doingSizing = FALSE;
\r
5181 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5182 GetClientRect(hwnd, &client);
\r
5183 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5185 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5187 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5190 case WM_DESTROY: /* message: window being destroyed */
\r
5191 PostQuitMessage(0);
\r
5195 if (hwnd == hwndMain) {
\r
5200 default: /* Passes it on if unprocessed */
\r
5201 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5206 /*---------------------------------------------------------------------------*\
\r
5208 * Misc utility routines
\r
5210 \*---------------------------------------------------------------------------*/
\r
5213 * Decent random number generator, at least not as bad as Windows
\r
5214 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5216 unsigned int randstate;
\r
5221 randstate = randstate * 1664525 + 1013904223;
\r
5222 return (int) randstate & 0x7fffffff;
\r
5226 mysrandom(unsigned int seed)
\r
5233 * returns TRUE if user selects a different color, FALSE otherwise
\r
5237 ChangeColor(HWND hwnd, COLORREF *which)
\r
5239 static BOOL firstTime = TRUE;
\r
5240 static DWORD customColors[16];
\r
5242 COLORREF newcolor;
\r
5247 /* Make initial colors in use available as custom colors */
\r
5248 /* Should we put the compiled-in defaults here instead? */
\r
5250 customColors[i++] = lightSquareColor & 0xffffff;
\r
5251 customColors[i++] = darkSquareColor & 0xffffff;
\r
5252 customColors[i++] = whitePieceColor & 0xffffff;
\r
5253 customColors[i++] = blackPieceColor & 0xffffff;
\r
5254 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5255 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5257 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5258 customColors[i++] = textAttribs[ccl].color;
\r
5260 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5261 firstTime = FALSE;
\r
5264 cc.lStructSize = sizeof(cc);
\r
5265 cc.hwndOwner = hwnd;
\r
5266 cc.hInstance = NULL;
\r
5267 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5268 cc.lpCustColors = (LPDWORD) customColors;
\r
5269 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5271 if (!ChooseColor(&cc)) return FALSE;
\r
5273 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5274 if (newcolor == *which) return FALSE;
\r
5275 *which = newcolor;
\r
5279 InitDrawingColors();
\r
5280 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5285 MyLoadSound(MySound *ms)
\r
5291 if (ms->data) free(ms->data);
\r
5294 switch (ms->name[0]) {
\r
5300 /* System sound from Control Panel. Don't preload here. */
\r
5304 if (ms->name[1] == NULLCHAR) {
\r
5305 /* "!" alone = silence */
\r
5308 /* Builtin wave resource. Error if not found. */
\r
5309 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5310 if (h == NULL) break;
\r
5311 ms->data = (void *)LoadResource(hInst, h);
\r
5312 if (h == NULL) break;
\r
5317 /* .wav file. Error if not found. */
\r
5318 f = fopen(ms->name, "rb");
\r
5319 if (f == NULL) break;
\r
5320 if (fstat(fileno(f), &st) < 0) break;
\r
5321 ms->data = malloc(st.st_size);
\r
5322 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5328 char buf[MSG_SIZ];
\r
5329 sprintf(buf, "Error loading sound %s", ms->name);
\r
5330 DisplayError(buf, GetLastError());
\r
5336 MyPlaySound(MySound *ms)
\r
5338 BOOLEAN ok = FALSE;
\r
5340 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5341 switch (ms->name[0]) {
\r
5343 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5348 /* System sound from Control Panel (deprecated feature).
\r
5349 "$" alone or an unset sound name gets default beep (still in use). */
\r
5350 if (ms->name[1]) {
\r
5351 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5353 if (!ok) ok = MessageBeep(MB_OK);
\r
5356 /* Builtin wave resource, or "!" alone for silence */
\r
5357 if (ms->name[1]) {
\r
5358 if (ms->data == NULL) return FALSE;
\r
5359 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5365 /* .wav file. Error if not found. */
\r
5366 if (ms->data == NULL) return FALSE;
\r
5367 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5370 /* Don't print an error: this can happen innocently if the sound driver
\r
5371 is busy; for instance, if another instance of WinBoard is playing
\r
5372 a sound at about the same time. */
\r
5378 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5381 OPENFILENAME *ofn;
\r
5382 static UINT *number; /* gross that this is static */
\r
5384 switch (message) {
\r
5385 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5386 /* Center the dialog over the application window */
\r
5387 ofn = (OPENFILENAME *) lParam;
\r
5388 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5389 number = (UINT *) ofn->lCustData;
\r
5390 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5394 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5395 return FALSE; /* Allow for further processing */
\r
5398 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5399 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5401 return FALSE; /* Allow for further processing */
\r
5407 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5409 static UINT *number;
\r
5410 OPENFILENAME *ofname;
\r
5413 case WM_INITDIALOG:
\r
5414 ofname = (OPENFILENAME *)lParam;
\r
5415 number = (UINT *)(ofname->lCustData);
\r
5418 ofnot = (OFNOTIFY *)lParam;
\r
5419 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5420 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5429 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5430 char *nameFilt, char *dlgTitle, UINT *number,
\r
5431 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5433 OPENFILENAME openFileName;
\r
5434 char buf1[MSG_SIZ];
\r
5437 if (fileName == NULL) fileName = buf1;
\r
5438 if (defName == NULL) {
\r
5439 strcpy(fileName, "*.");
\r
5440 strcat(fileName, defExt);
\r
5442 strcpy(fileName, defName);
\r
5444 if (fileTitle) strcpy(fileTitle, "");
\r
5445 if (number) *number = 0;
\r
5447 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5448 openFileName.hwndOwner = hwnd;
\r
5449 openFileName.hInstance = (HANDLE) hInst;
\r
5450 openFileName.lpstrFilter = nameFilt;
\r
5451 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5452 openFileName.nMaxCustFilter = 0L;
\r
5453 openFileName.nFilterIndex = 1L;
\r
5454 openFileName.lpstrFile = fileName;
\r
5455 openFileName.nMaxFile = MSG_SIZ;
\r
5456 openFileName.lpstrFileTitle = fileTitle;
\r
5457 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5458 openFileName.lpstrInitialDir = NULL;
\r
5459 openFileName.lpstrTitle = dlgTitle;
\r
5460 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5461 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5462 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5463 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5464 openFileName.nFileOffset = 0;
\r
5465 openFileName.nFileExtension = 0;
\r
5466 openFileName.lpstrDefExt = defExt;
\r
5467 openFileName.lCustData = (LONG) number;
\r
5468 openFileName.lpfnHook = oldDialog ?
\r
5469 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5470 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5472 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5473 GetOpenFileName(&openFileName)) {
\r
5474 /* open the file */
\r
5475 f = fopen(openFileName.lpstrFile, write);
\r
5477 MessageBox(hwnd, "File open failed", NULL,
\r
5478 MB_OK|MB_ICONEXCLAMATION);
\r
5482 int err = CommDlgExtendedError();
\r
5483 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5492 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5494 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5497 * Get the first pop-up menu in the menu template. This is the
\r
5498 * menu that TrackPopupMenu displays.
\r
5500 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5502 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5505 * TrackPopup uses screen coordinates, so convert the
\r
5506 * coordinates of the mouse click to screen coordinates.
\r
5508 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5510 /* Draw and track the floating pop-up menu. */
\r
5511 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5512 pt.x, pt.y, 0, hwnd, NULL);
\r
5514 /* Destroy the menu.*/
\r
5515 DestroyMenu(hmenu);
\r
5520 int sizeX, sizeY, newSizeX, newSizeY;
\r
5522 } ResizeEditPlusButtonsClosure;
\r
5525 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5527 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5531 if (hChild == cl->hText) return TRUE;
\r
5532 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5533 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5534 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5535 ScreenToClient(cl->hDlg, &pt);
\r
5536 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5537 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5541 /* Resize a dialog that has a (rich) edit field filling most of
\r
5542 the top, with a row of buttons below */
\r
5544 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5547 int newTextHeight, newTextWidth;
\r
5548 ResizeEditPlusButtonsClosure cl;
\r
5550 /*if (IsIconic(hDlg)) return;*/
\r
5551 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5553 cl.hdwp = BeginDeferWindowPos(8);
\r
5555 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5556 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5557 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5558 if (newTextHeight < 0) {
\r
5559 newSizeY += -newTextHeight;
\r
5560 newTextHeight = 0;
\r
5562 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5563 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5569 cl.newSizeX = newSizeX;
\r
5570 cl.newSizeY = newSizeY;
\r
5571 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5573 EndDeferWindowPos(cl.hdwp);
\r
5576 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5578 RECT rChild, rParent;
\r
5579 int wChild, hChild, wParent, hParent;
\r
5580 int wScreen, hScreen, xNew, yNew;
\r
5583 /* Get the Height and Width of the child window */
\r
5584 GetWindowRect (hwndChild, &rChild);
\r
5585 wChild = rChild.right - rChild.left;
\r
5586 hChild = rChild.bottom - rChild.top;
\r
5588 /* Get the Height and Width of the parent window */
\r
5589 GetWindowRect (hwndParent, &rParent);
\r
5590 wParent = rParent.right - rParent.left;
\r
5591 hParent = rParent.bottom - rParent.top;
\r
5593 /* Get the display limits */
\r
5594 hdc = GetDC (hwndChild);
\r
5595 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5596 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5597 ReleaseDC(hwndChild, hdc);
\r
5599 /* Calculate new X position, then adjust for screen */
\r
5600 xNew = rParent.left + ((wParent - wChild) /2);
\r
5603 } else if ((xNew+wChild) > wScreen) {
\r
5604 xNew = wScreen - wChild;
\r
5607 /* Calculate new Y position, then adjust for screen */
\r
5609 yNew = rParent.top + ((hParent - hChild) /2);
\r
5612 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5617 } else if ((yNew+hChild) > hScreen) {
\r
5618 yNew = hScreen - hChild;
\r
5621 /* Set it, and return */
\r
5622 return SetWindowPos (hwndChild, NULL,
\r
5623 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5626 /* Center one window over another */
\r
5627 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5629 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5632 /*---------------------------------------------------------------------------*\
\r
5634 * Startup Dialog functions
\r
5636 \*---------------------------------------------------------------------------*/
\r
5638 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5640 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5642 while (*cd != NULL) {
\r
5643 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5649 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5651 char buf1[MAX_ARG_LEN];
\r
5654 if (str[0] == '@') {
\r
5655 FILE* f = fopen(str + 1, "r");
\r
5657 DisplayFatalError(str + 1, errno, 2);
\r
5660 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5662 buf1[len] = NULLCHAR;
\r
5666 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5669 char buf[MSG_SIZ];
\r
5670 char *end = strchr(str, '\n');
\r
5671 if (end == NULL) return;
\r
5672 memcpy(buf, str, end - str);
\r
5673 buf[end - str] = NULLCHAR;
\r
5674 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5680 SetStartupDialogEnables(HWND hDlg)
\r
5682 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5683 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5684 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5685 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5686 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5687 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5688 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5689 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5690 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5691 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5692 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5693 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5694 IsDlgButtonChecked(hDlg, OPT_View));
\r
5698 QuoteForFilename(char *filename)
\r
5700 int dquote, space;
\r
5701 dquote = strchr(filename, '"') != NULL;
\r
5702 space = strchr(filename, ' ') != NULL;
\r
5703 if (dquote || space) {
\r
5715 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5717 char buf[MSG_SIZ];
\r
5720 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5721 q = QuoteForFilename(nthcp);
\r
5722 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5723 if (*nthdir != NULLCHAR) {
\r
5724 q = QuoteForFilename(nthdir);
\r
5725 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5727 if (*nthcp == NULLCHAR) {
\r
5728 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5729 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5730 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5731 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5736 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5738 char buf[MSG_SIZ];
\r
5742 switch (message) {
\r
5743 case WM_INITDIALOG:
\r
5744 /* Center the dialog */
\r
5745 CenterWindow (hDlg, GetDesktopWindow());
\r
5746 /* Initialize the dialog items */
\r
5747 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5748 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5749 firstChessProgramNames);
\r
5750 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5751 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5752 secondChessProgramNames);
\r
5753 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5754 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5755 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5756 if (*appData.icsHelper != NULLCHAR) {
\r
5757 char *q = QuoteForFilename(appData.icsHelper);
\r
5758 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5760 if (*appData.icsHost == NULLCHAR) {
\r
5761 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5762 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5763 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5764 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5765 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5768 if (appData.icsActive) {
\r
5769 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5771 else if (appData.noChessProgram) {
\r
5772 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5775 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5778 SetStartupDialogEnables(hDlg);
\r
5782 switch (LOWORD(wParam)) {
\r
5784 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5785 strcpy(buf, "/fcp=");
\r
5786 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5788 ParseArgs(StringGet, &p);
\r
5789 strcpy(buf, "/scp=");
\r
5790 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5792 ParseArgs(StringGet, &p);
\r
5793 appData.noChessProgram = FALSE;
\r
5794 appData.icsActive = FALSE;
\r
5795 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5796 strcpy(buf, "/ics /icshost=");
\r
5797 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5799 ParseArgs(StringGet, &p);
\r
5800 if (appData.zippyPlay) {
\r
5801 strcpy(buf, "/fcp=");
\r
5802 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5804 ParseArgs(StringGet, &p);
\r
5806 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5807 appData.noChessProgram = TRUE;
\r
5808 appData.icsActive = FALSE;
\r
5810 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5811 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5814 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5815 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5817 ParseArgs(StringGet, &p);
\r
5819 EndDialog(hDlg, TRUE);
\r
5826 case IDM_HELPCONTENTS:
\r
5827 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5828 MessageBox (GetFocus(),
\r
5829 "Unable to activate help",
\r
5830 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5835 SetStartupDialogEnables(hDlg);
\r
5843 /*---------------------------------------------------------------------------*\
\r
5845 * About box dialog functions
\r
5847 \*---------------------------------------------------------------------------*/
\r
5849 /* Process messages for "About" dialog box */
\r
5851 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5853 switch (message) {
\r
5854 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5855 /* Center the dialog over the application window */
\r
5856 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5857 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5861 case WM_COMMAND: /* message: received a command */
\r
5862 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5863 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5864 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5872 /*---------------------------------------------------------------------------*\
\r
5874 * Comment Dialog functions
\r
5876 \*---------------------------------------------------------------------------*/
\r
5879 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5881 static HANDLE hwndText = NULL;
\r
5882 int len, newSizeX, newSizeY, flags;
\r
5883 static int sizeX, sizeY;
\r
5888 switch (message) {
\r
5889 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5890 /* Initialize the dialog items */
\r
5891 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5892 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5893 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5894 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5895 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5896 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5897 SetWindowText(hDlg, commentTitle);
\r
5898 if (editComment) {
\r
5899 SetFocus(hwndText);
\r
5901 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5903 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5904 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5905 MAKELPARAM(FALSE, 0));
\r
5906 /* Size and position the dialog */
\r
5907 if (!commentDialog) {
\r
5908 commentDialog = hDlg;
\r
5909 flags = SWP_NOZORDER;
\r
5910 GetClientRect(hDlg, &rect);
\r
5911 sizeX = rect.right;
\r
5912 sizeY = rect.bottom;
\r
5913 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5914 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5915 WINDOWPLACEMENT wp;
\r
5916 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5917 wp.length = sizeof(WINDOWPLACEMENT);
\r
5919 wp.showCmd = SW_SHOW;
\r
5920 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5921 wp.rcNormalPosition.left = wpComment.x;
\r
5922 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5923 wp.rcNormalPosition.top = wpComment.y;
\r
5924 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5925 SetWindowPlacement(hDlg, &wp);
\r
5927 GetClientRect(hDlg, &rect);
\r
5928 newSizeX = rect.right;
\r
5929 newSizeY = rect.bottom;
\r
5930 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5931 newSizeX, newSizeY);
\r
5936 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5939 case WM_COMMAND: /* message: received a command */
\r
5940 switch (LOWORD(wParam)) {
\r
5942 if (editComment) {
\r
5944 /* Read changed options from the dialog box */
\r
5945 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5946 len = GetWindowTextLength(hwndText);
\r
5947 str = (char *) malloc(len + 1);
\r
5948 GetWindowText(hwndText, str, len + 1);
\r
5957 ReplaceComment(commentIndex, str);
\r
5964 case OPT_CancelComment:
\r
5968 case OPT_ClearComment:
\r
5969 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5972 case OPT_EditComment:
\r
5973 EditCommentEvent();
\r
5981 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5982 if( wParam == OPT_CommentText ) {
\r
5983 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5985 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5989 pt.x = LOWORD( lpMF->lParam );
\r
5990 pt.y = HIWORD( lpMF->lParam );
\r
5992 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5994 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5995 len = GetWindowTextLength(hwndText);
\r
5996 str = (char *) malloc(len + 1);
\r
5997 GetWindowText(hwndText, str, len + 1);
\r
5998 ReplaceComment(commentIndex, str);
\r
5999 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6000 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6003 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6004 lpMF->msg = WM_USER;
\r
6012 newSizeX = LOWORD(lParam);
\r
6013 newSizeY = HIWORD(lParam);
\r
6014 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6019 case WM_GETMINMAXINFO:
\r
6020 /* Prevent resizing window too small */
\r
6021 mmi = (MINMAXINFO *) lParam;
\r
6022 mmi->ptMinTrackSize.x = 100;
\r
6023 mmi->ptMinTrackSize.y = 100;
\r
6030 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6035 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6037 if (str == NULL) str = "";
\r
6038 p = (char *) malloc(2 * strlen(str) + 2);
\r
6041 if (*str == '\n') *q++ = '\r';
\r
6045 if (commentText != NULL) free(commentText);
\r
6047 commentIndex = index;
\r
6048 commentTitle = title;
\r
6050 editComment = edit;
\r
6052 if (commentDialog) {
\r
6053 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6054 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6056 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6057 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6058 hwndMain, (DLGPROC)lpProc);
\r
6059 FreeProcInstance(lpProc);
\r
6065 /*---------------------------------------------------------------------------*\
\r
6067 * Type-in move dialog functions
\r
6069 \*---------------------------------------------------------------------------*/
\r
6072 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6074 char move[MSG_SIZ];
\r
6076 ChessMove moveType;
\r
6077 int fromX, fromY, toX, toY;
\r
6080 switch (message) {
\r
6081 case WM_INITDIALOG:
\r
6082 move[0] = (char) lParam;
\r
6083 move[1] = NULLCHAR;
\r
6084 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6085 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6086 SetWindowText(hInput, move);
\r
6088 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6092 switch (LOWORD(wParam)) {
\r
6094 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6095 { int n; Board board;
\r
6097 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6098 EditPositionPasteFEN(move);
\r
6099 EndDialog(hDlg, TRUE);
\r
6102 // [HGM] movenum: allow move number to be typed in any mode
\r
6103 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6105 EndDialog(hDlg, TRUE);
\r
6109 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6110 gameMode != Training) {
\r
6111 DisplayMoveError("Displayed move is not current");
\r
6113 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6114 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6115 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6116 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6117 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6118 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6119 if (gameMode != Training)
\r
6120 forwardMostMove = currentMove;
\r
6121 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6123 DisplayMoveError("Could not parse move");
\r
6126 EndDialog(hDlg, TRUE);
\r
6129 EndDialog(hDlg, FALSE);
\r
6140 PopUpMoveDialog(char firstchar)
\r
6144 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6145 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6146 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6147 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6148 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6149 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6150 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6151 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6152 gameMode == Training) {
\r
6153 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6154 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6155 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6156 FreeProcInstance(lpProc);
\r
6160 /*---------------------------------------------------------------------------*\
\r
6162 * Type-in name dialog functions
\r
6164 \*---------------------------------------------------------------------------*/
\r
6167 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6169 char move[MSG_SIZ];
\r
6172 switch (message) {
\r
6173 case WM_INITDIALOG:
\r
6174 move[0] = (char) lParam;
\r
6175 move[1] = NULLCHAR;
\r
6176 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6177 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6178 SetWindowText(hInput, move);
\r
6180 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6184 switch (LOWORD(wParam)) {
\r
6186 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6187 appData.userName = strdup(move);
\r
6190 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6191 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6192 DisplayTitle(move);
\r
6196 EndDialog(hDlg, TRUE);
\r
6199 EndDialog(hDlg, FALSE);
\r
6210 PopUpNameDialog(char firstchar)
\r
6214 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6215 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6216 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6217 FreeProcInstance(lpProc);
\r
6220 /*---------------------------------------------------------------------------*\
\r
6224 \*---------------------------------------------------------------------------*/
\r
6226 /* Nonmodal error box */
\r
6227 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6228 WPARAM wParam, LPARAM lParam);
\r
6231 ErrorPopUp(char *title, char *content)
\r
6235 BOOLEAN modal = hwndMain == NULL;
\r
6253 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6254 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6257 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6259 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6260 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6261 hwndMain, (DLGPROC)lpProc);
\r
6262 FreeProcInstance(lpProc);
\r
6269 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6270 if (errorDialog == NULL) return;
\r
6271 DestroyWindow(errorDialog);
\r
6272 errorDialog = NULL;
\r
6273 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6277 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6282 switch (message) {
\r
6283 case WM_INITDIALOG:
\r
6284 GetWindowRect(hDlg, &rChild);
\r
6287 SetWindowPos(hDlg, NULL, rChild.left,
\r
6288 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6289 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6293 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6294 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6295 and it doesn't work when you resize the dialog.
\r
6296 For now, just give it a default position.
\r
6298 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6300 errorDialog = hDlg;
\r
6301 SetWindowText(hDlg, errorTitle);
\r
6302 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6303 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6307 switch (LOWORD(wParam)) {
\r
6310 if (errorDialog == hDlg) errorDialog = NULL;
\r
6311 DestroyWindow(hDlg);
\r
6323 HWND gothicDialog = NULL;
\r
6326 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6330 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6332 switch (message) {
\r
6333 case WM_INITDIALOG:
\r
6334 GetWindowRect(hDlg, &rChild);
\r
6336 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6340 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6341 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6342 and it doesn't work when you resize the dialog.
\r
6343 For now, just give it a default position.
\r
6345 gothicDialog = hDlg;
\r
6346 SetWindowText(hDlg, errorTitle);
\r
6347 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6348 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6352 switch (LOWORD(wParam)) {
\r
6355 if (errorDialog == hDlg) errorDialog = NULL;
\r
6356 DestroyWindow(hDlg);
\r
6368 GothicPopUp(char *title, VariantClass variant)
\r
6371 static char *lastTitle;
\r
6373 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6374 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6376 if(lastTitle != title && gothicDialog != NULL) {
\r
6377 DestroyWindow(gothicDialog);
\r
6378 gothicDialog = NULL;
\r
6380 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6381 title = lastTitle;
\r
6382 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6383 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6384 hwndMain, (DLGPROC)lpProc);
\r
6385 FreeProcInstance(lpProc);
\r
6390 /*---------------------------------------------------------------------------*\
\r
6392 * Ics Interaction console functions
\r
6394 \*---------------------------------------------------------------------------*/
\r
6396 #define HISTORY_SIZE 64
\r
6397 static char *history[HISTORY_SIZE];
\r
6398 int histIn = 0, histP = 0;
\r
6401 SaveInHistory(char *cmd)
\r
6403 if (history[histIn] != NULL) {
\r
6404 free(history[histIn]);
\r
6405 history[histIn] = NULL;
\r
6407 if (*cmd == NULLCHAR) return;
\r
6408 history[histIn] = StrSave(cmd);
\r
6409 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6410 if (history[histIn] != NULL) {
\r
6411 free(history[histIn]);
\r
6412 history[histIn] = NULL;
\r
6418 PrevInHistory(char *cmd)
\r
6421 if (histP == histIn) {
\r
6422 if (history[histIn] != NULL) free(history[histIn]);
\r
6423 history[histIn] = StrSave(cmd);
\r
6425 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6426 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6428 return history[histP];
\r
6434 if (histP == histIn) return NULL;
\r
6435 histP = (histP + 1) % HISTORY_SIZE;
\r
6436 return history[histP];
\r
6440 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6444 hmenu = LoadMenu(hInst, "TextMenu");
\r
6445 h = GetSubMenu(hmenu, 0);
\r
6447 if (strcmp(e->item, "-") == 0) {
\r
6448 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6449 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6450 int flags = MF_STRING, j = 0;
\r
6451 if (e->item[0] == '|') {
\r
6452 flags |= MF_MENUBARBREAK;
\r
6455 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6456 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6464 WNDPROC consoleTextWindowProc;
\r
6467 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6469 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6470 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6474 SetWindowText(hInput, command);
\r
6476 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6478 sel.cpMin = 999999;
\r
6479 sel.cpMax = 999999;
\r
6480 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6485 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6486 if (sel.cpMin == sel.cpMax) {
\r
6487 /* Expand to surrounding word */
\r
6490 tr.chrg.cpMax = sel.cpMin;
\r
6491 tr.chrg.cpMin = --sel.cpMin;
\r
6492 if (sel.cpMin < 0) break;
\r
6493 tr.lpstrText = name;
\r
6494 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6495 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6499 tr.chrg.cpMin = sel.cpMax;
\r
6500 tr.chrg.cpMax = ++sel.cpMax;
\r
6501 tr.lpstrText = name;
\r
6502 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6503 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6506 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6507 MessageBeep(MB_ICONEXCLAMATION);
\r
6511 tr.lpstrText = name;
\r
6512 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6514 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6515 MessageBeep(MB_ICONEXCLAMATION);
\r
6518 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6521 sprintf(buf, "%s %s", command, name);
\r
6522 SetWindowText(hInput, buf);
\r
6523 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6525 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6526 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6527 SetWindowText(hInput, buf);
\r
6528 sel.cpMin = 999999;
\r
6529 sel.cpMax = 999999;
\r
6530 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6536 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6541 switch (message) {
\r
6543 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6546 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6549 sel.cpMin = 999999;
\r
6550 sel.cpMax = 999999;
\r
6551 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6552 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6557 if(wParam != '\022') {
\r
6558 if (wParam == '\t') {
\r
6559 if (GetKeyState(VK_SHIFT) < 0) {
\r
6561 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6562 if (buttonDesc[0].hwnd) {
\r
6563 SetFocus(buttonDesc[0].hwnd);
\r
6565 SetFocus(hwndMain);
\r
6569 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6572 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6573 JAWS_DELETE( SetFocus(hInput); )
\r
6574 SendMessage(hInput, message, wParam, lParam);
\r
6577 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6578 case WM_RBUTTONDOWN:
\r
6579 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6580 /* Move selection here if it was empty */
\r
6582 pt.x = LOWORD(lParam);
\r
6583 pt.y = HIWORD(lParam);
\r
6584 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6585 if (sel.cpMin == sel.cpMax) {
\r
6586 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6587 sel.cpMax = sel.cpMin;
\r
6588 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6590 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6591 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6593 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6594 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6595 if (sel.cpMin == sel.cpMax) {
\r
6596 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6597 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6599 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6600 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6602 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6603 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6604 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6605 MenuPopup(hwnd, pt, hmenu, -1);
\r
6609 case WM_RBUTTONUP:
\r
6610 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6611 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6612 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6616 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6618 return SendMessage(hInput, message, wParam, lParam);
\r
6619 case WM_MBUTTONDOWN:
\r
6620 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6622 switch (LOWORD(wParam)) {
\r
6623 case IDM_QuickPaste:
\r
6625 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6626 if (sel.cpMin == sel.cpMax) {
\r
6627 MessageBeep(MB_ICONEXCLAMATION);
\r
6630 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6631 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6632 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6637 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6640 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6643 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6647 int i = LOWORD(wParam) - IDM_CommandX;
\r
6648 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6649 icsTextMenuEntry[i].command != NULL) {
\r
6650 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6651 icsTextMenuEntry[i].getname,
\r
6652 icsTextMenuEntry[i].immediate);
\r
6660 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6663 WNDPROC consoleInputWindowProc;
\r
6666 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6668 char buf[MSG_SIZ];
\r
6670 static BOOL sendNextChar = FALSE;
\r
6671 static BOOL quoteNextChar = FALSE;
\r
6672 InputSource *is = consoleInputSource;
\r
6676 switch (message) {
\r
6678 if (!appData.localLineEditing || sendNextChar) {
\r
6679 is->buf[0] = (CHAR) wParam;
\r
6681 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6682 sendNextChar = FALSE;
\r
6685 if (quoteNextChar) {
\r
6686 buf[0] = (char) wParam;
\r
6687 buf[1] = NULLCHAR;
\r
6688 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6689 quoteNextChar = FALSE;
\r
6693 case '\r': /* Enter key */
\r
6694 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6695 if (consoleEcho) SaveInHistory(is->buf);
\r
6696 is->buf[is->count++] = '\n';
\r
6697 is->buf[is->count] = NULLCHAR;
\r
6698 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6699 if (consoleEcho) {
\r
6700 ConsoleOutput(is->buf, is->count, TRUE);
\r
6701 } else if (appData.localLineEditing) {
\r
6702 ConsoleOutput("\n", 1, TRUE);
\r
6705 case '\033': /* Escape key */
\r
6706 SetWindowText(hwnd, "");
\r
6707 cf.cbSize = sizeof(CHARFORMAT);
\r
6708 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6709 if (consoleEcho) {
\r
6710 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6712 cf.crTextColor = COLOR_ECHOOFF;
\r
6714 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6715 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6717 case '\t': /* Tab key */
\r
6718 if (GetKeyState(VK_SHIFT) < 0) {
\r
6720 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6723 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6724 if (buttonDesc[0].hwnd) {
\r
6725 SetFocus(buttonDesc[0].hwnd);
\r
6727 SetFocus(hwndMain);
\r
6731 case '\023': /* Ctrl+S */
\r
6732 sendNextChar = TRUE;
\r
6734 case '\021': /* Ctrl+Q */
\r
6735 quoteNextChar = TRUE;
\r
6745 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6746 p = PrevInHistory(buf);
\r
6748 SetWindowText(hwnd, p);
\r
6749 sel.cpMin = 999999;
\r
6750 sel.cpMax = 999999;
\r
6751 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6756 p = NextInHistory();
\r
6758 SetWindowText(hwnd, p);
\r
6759 sel.cpMin = 999999;
\r
6760 sel.cpMax = 999999;
\r
6761 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6767 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6771 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6775 case WM_MBUTTONDOWN:
\r
6776 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6777 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6779 case WM_RBUTTONUP:
\r
6780 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6781 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6782 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6786 hmenu = LoadMenu(hInst, "InputMenu");
\r
6787 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6788 if (sel.cpMin == sel.cpMax) {
\r
6789 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6790 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6792 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6793 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6795 pt.x = LOWORD(lParam);
\r
6796 pt.y = HIWORD(lParam);
\r
6797 MenuPopup(hwnd, pt, hmenu, -1);
\r
6801 switch (LOWORD(wParam)) {
\r
6803 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6805 case IDM_SelectAll:
\r
6807 sel.cpMax = -1; /*999999?*/
\r
6808 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6811 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6814 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6817 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6822 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6825 #define CO_MAX 100000
\r
6826 #define CO_TRIM 1000
\r
6829 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6831 static SnapData sd;
\r
6832 HWND hText, hInput;
\r
6834 static int sizeX, sizeY;
\r
6835 int newSizeX, newSizeY;
\r
6839 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6840 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6842 switch (message) {
\r
6844 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6846 ENLINK *pLink = (ENLINK*)lParam;
\r
6847 if (pLink->msg == WM_LBUTTONUP)
\r
6851 tr.chrg = pLink->chrg;
\r
6852 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6853 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6854 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6855 free(tr.lpstrText);
\r
6859 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6860 hwndConsole = hDlg;
\r
6862 consoleTextWindowProc = (WNDPROC)
\r
6863 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6864 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6865 consoleInputWindowProc = (WNDPROC)
\r
6866 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6867 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6868 Colorize(ColorNormal, TRUE);
\r
6869 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6870 ChangedConsoleFont();
\r
6871 GetClientRect(hDlg, &rect);
\r
6872 sizeX = rect.right;
\r
6873 sizeY = rect.bottom;
\r
6874 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6875 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6876 WINDOWPLACEMENT wp;
\r
6877 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6878 wp.length = sizeof(WINDOWPLACEMENT);
\r
6880 wp.showCmd = SW_SHOW;
\r
6881 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6882 wp.rcNormalPosition.left = wpConsole.x;
\r
6883 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6884 wp.rcNormalPosition.top = wpConsole.y;
\r
6885 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6886 SetWindowPlacement(hDlg, &wp);
\r
6889 // [HGM] Chessknight's change 2004-07-13
\r
6890 else { /* Determine Defaults */
\r
6891 WINDOWPLACEMENT wp;
\r
6892 wpConsole.x = wpMain.width + 1;
\r
6893 wpConsole.y = wpMain.y;
\r
6894 wpConsole.width = screenWidth - wpMain.width;
\r
6895 wpConsole.height = wpMain.height;
\r
6896 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6897 wp.length = sizeof(WINDOWPLACEMENT);
\r
6899 wp.showCmd = SW_SHOW;
\r
6900 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6901 wp.rcNormalPosition.left = wpConsole.x;
\r
6902 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6903 wp.rcNormalPosition.top = wpConsole.y;
\r
6904 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6905 SetWindowPlacement(hDlg, &wp);
\r
6908 // Allow hText to highlight URLs and send notifications on them
\r
6909 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6910 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6911 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6912 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6926 if (IsIconic(hDlg)) break;
\r
6927 newSizeX = LOWORD(lParam);
\r
6928 newSizeY = HIWORD(lParam);
\r
6929 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6930 RECT rectText, rectInput;
\r
6932 int newTextHeight, newTextWidth;
\r
6933 GetWindowRect(hText, &rectText);
\r
6934 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6935 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6936 if (newTextHeight < 0) {
\r
6937 newSizeY += -newTextHeight;
\r
6938 newTextHeight = 0;
\r
6940 SetWindowPos(hText, NULL, 0, 0,
\r
6941 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6942 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6943 pt.x = rectInput.left;
\r
6944 pt.y = rectInput.top + newSizeY - sizeY;
\r
6945 ScreenToClient(hDlg, &pt);
\r
6946 SetWindowPos(hInput, NULL,
\r
6947 pt.x, pt.y, /* needs client coords */
\r
6948 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6949 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6955 case WM_GETMINMAXINFO:
\r
6956 /* Prevent resizing window too small */
\r
6957 mmi = (MINMAXINFO *) lParam;
\r
6958 mmi->ptMinTrackSize.x = 100;
\r
6959 mmi->ptMinTrackSize.y = 100;
\r
6962 /* [AS] Snapping */
\r
6963 case WM_ENTERSIZEMOVE:
\r
6964 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6967 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6970 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6972 case WM_EXITSIZEMOVE:
\r
6973 UpdateICSWidth(hText);
\r
6974 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6977 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6985 if (hwndConsole) return;
\r
6986 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6987 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6992 ConsoleOutput(char* data, int length, int forceVisible)
\r
6997 char buf[CO_MAX+1];
\r
7000 static int delayLF = 0;
\r
7001 CHARRANGE savesel, sel;
\r
7003 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7011 while (length--) {
\r
7019 } else if (*p == '\007') {
\r
7020 MyPlaySound(&sounds[(int)SoundBell]);
\r
7027 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7028 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7029 /* Save current selection */
\r
7030 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7031 exlen = GetWindowTextLength(hText);
\r
7032 /* Find out whether current end of text is visible */
\r
7033 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7034 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7035 /* Trim existing text if it's too long */
\r
7036 if (exlen + (q - buf) > CO_MAX) {
\r
7037 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7040 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7041 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7043 savesel.cpMin -= trim;
\r
7044 savesel.cpMax -= trim;
\r
7045 if (exlen < 0) exlen = 0;
\r
7046 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7047 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7049 /* Append the new text */
\r
7050 sel.cpMin = exlen;
\r
7051 sel.cpMax = exlen;
\r
7052 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7053 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7054 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7055 if (forceVisible || exlen == 0 ||
\r
7056 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7057 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7058 /* Scroll to make new end of text visible if old end of text
\r
7059 was visible or new text is an echo of user typein */
\r
7060 sel.cpMin = 9999999;
\r
7061 sel.cpMax = 9999999;
\r
7062 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7063 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7064 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7065 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7067 if (savesel.cpMax == exlen || forceVisible) {
\r
7068 /* Move insert point to new end of text if it was at the old
\r
7069 end of text or if the new text is an echo of user typein */
\r
7070 sel.cpMin = 9999999;
\r
7071 sel.cpMax = 9999999;
\r
7072 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7074 /* Restore previous selection */
\r
7075 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7077 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7084 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7088 COLORREF oldFg, oldBg;
\r
7092 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7094 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7095 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7096 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7099 rect.right = x + squareSize;
\r
7101 rect.bottom = y + squareSize;
\r
7104 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7105 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7106 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7107 &rect, str, strlen(str), NULL);
\r
7109 (void) SetTextColor(hdc, oldFg);
\r
7110 (void) SetBkColor(hdc, oldBg);
\r
7111 (void) SelectObject(hdc, oldFont);
\r
7115 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7116 RECT *rect, char *color, char *flagFell)
\r
7120 COLORREF oldFg, oldBg;
\r
7123 if (appData.clockMode) {
\r
7125 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7127 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7134 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7135 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7137 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7138 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7140 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7144 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7145 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7146 rect, str, strlen(str), NULL);
\r
7147 if(logoHeight > 0 && appData.clockMode) {
\r
7149 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7150 r.top = rect->top + logoHeight/2;
\r
7151 r.left = rect->left;
\r
7152 r.right = rect->right;
\r
7153 r.bottom = rect->bottom;
\r
7154 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7155 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7156 &r, str, strlen(str), NULL);
\r
7158 (void) SetTextColor(hdc, oldFg);
\r
7159 (void) SetBkColor(hdc, oldBg);
\r
7160 (void) SelectObject(hdc, oldFont);
\r
7165 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7171 if( count <= 0 ) {
\r
7172 if (appData.debugMode) {
\r
7173 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7176 return ERROR_INVALID_USER_BUFFER;
\r
7179 ResetEvent(ovl->hEvent);
\r
7180 ovl->Offset = ovl->OffsetHigh = 0;
\r
7181 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7185 err = GetLastError();
\r
7186 if (err == ERROR_IO_PENDING) {
\r
7187 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7191 err = GetLastError();
\r
7198 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7203 ResetEvent(ovl->hEvent);
\r
7204 ovl->Offset = ovl->OffsetHigh = 0;
\r
7205 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7209 err = GetLastError();
\r
7210 if (err == ERROR_IO_PENDING) {
\r
7211 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7215 err = GetLastError();
\r
7221 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7222 void CheckForInputBufferFull( InputSource * is )
\r
7224 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7225 /* Look for end of line */
\r
7226 char * p = is->buf;
\r
7228 while( p < is->next && *p != '\n' ) {
\r
7232 if( p >= is->next ) {
\r
7233 if (appData.debugMode) {
\r
7234 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7237 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7238 is->count = (DWORD) -1;
\r
7239 is->next = is->buf;
\r
7245 InputThread(LPVOID arg)
\r
7250 is = (InputSource *) arg;
\r
7251 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7252 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7253 while (is->hThread != NULL) {
\r
7254 is->error = DoReadFile(is->hFile, is->next,
\r
7255 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7256 &is->count, &ovl);
\r
7257 if (is->error == NO_ERROR) {
\r
7258 is->next += is->count;
\r
7260 if (is->error == ERROR_BROKEN_PIPE) {
\r
7261 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7264 is->count = (DWORD) -1;
\r
7265 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7270 CheckForInputBufferFull( is );
\r
7272 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7274 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7276 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7279 CloseHandle(ovl.hEvent);
\r
7280 CloseHandle(is->hFile);
\r
7282 if (appData.debugMode) {
\r
7283 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7290 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7292 NonOvlInputThread(LPVOID arg)
\r
7299 is = (InputSource *) arg;
\r
7300 while (is->hThread != NULL) {
\r
7301 is->error = ReadFile(is->hFile, is->next,
\r
7302 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7303 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7304 if (is->error == NO_ERROR) {
\r
7305 /* Change CRLF to LF */
\r
7306 if (is->next > is->buf) {
\r
7308 i = is->count + 1;
\r
7316 if (prev == '\r' && *p == '\n') {
\r
7328 if (is->error == ERROR_BROKEN_PIPE) {
\r
7329 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7332 is->count = (DWORD) -1;
\r
7336 CheckForInputBufferFull( is );
\r
7338 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7340 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7342 if (is->count < 0) break; /* Quit on error */
\r
7344 CloseHandle(is->hFile);
\r
7349 SocketInputThread(LPVOID arg)
\r
7353 is = (InputSource *) arg;
\r
7354 while (is->hThread != NULL) {
\r
7355 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7356 if ((int)is->count == SOCKET_ERROR) {
\r
7357 is->count = (DWORD) -1;
\r
7358 is->error = WSAGetLastError();
\r
7360 is->error = NO_ERROR;
\r
7361 is->next += is->count;
\r
7362 if (is->count == 0 && is->second == is) {
\r
7363 /* End of file on stderr; quit with no message */
\r
7367 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7369 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7371 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7377 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7381 is = (InputSource *) lParam;
\r
7382 if (is->lineByLine) {
\r
7383 /* Feed in lines one by one */
\r
7384 char *p = is->buf;
\r
7386 while (q < is->next) {
\r
7387 if (*q++ == '\n') {
\r
7388 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7393 /* Move any partial line to the start of the buffer */
\r
7395 while (p < is->next) {
\r
7400 if (is->error != NO_ERROR || is->count == 0) {
\r
7401 /* Notify backend of the error. Note: If there was a partial
\r
7402 line at the end, it is not flushed through. */
\r
7403 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7406 /* Feed in the whole chunk of input at once */
\r
7407 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7408 is->next = is->buf;
\r
7412 /*---------------------------------------------------------------------------*\
\r
7414 * Menu enables. Used when setting various modes.
\r
7416 \*---------------------------------------------------------------------------*/
\r
7424 GreyRevert(Boolean grey)
\r
7425 { // [HGM] vari: for retracting variations in local mode
\r
7426 HMENU hmenu = GetMenu(hwndMain);
\r
7427 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7428 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7432 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7434 while (enab->item > 0) {
\r
7435 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7440 Enables gnuEnables[] = {
\r
7441 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7442 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7443 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7444 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7445 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7446 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7447 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7448 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7449 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7450 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7451 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7452 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7453 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7457 Enables icsEnables[] = {
\r
7458 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7459 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7460 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7461 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7463 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7464 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7465 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7466 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7467 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7471 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7472 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7473 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7474 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7479 Enables zippyEnables[] = {
\r
7480 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7481 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7482 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7483 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7488 Enables ncpEnables[] = {
\r
7489 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7490 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7491 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7492 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7493 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7494 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7495 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7496 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7497 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7498 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7499 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7500 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7501 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7502 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7503 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7504 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7505 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7506 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7507 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7508 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7509 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7513 Enables trainingOnEnables[] = {
\r
7514 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7515 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7516 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7517 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7518 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7519 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7520 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7521 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7525 Enables trainingOffEnables[] = {
\r
7526 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7527 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7528 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7529 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7530 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7531 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7532 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7533 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7537 /* These modify either ncpEnables or gnuEnables */
\r
7538 Enables cmailEnables[] = {
\r
7539 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7540 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7541 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7542 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7543 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7544 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7545 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7549 Enables machineThinkingEnables[] = {
\r
7550 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7551 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7552 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7553 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7554 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7555 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7556 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7557 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7558 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7559 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7560 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7561 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7562 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7563 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7564 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7565 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7569 Enables userThinkingEnables[] = {
\r
7570 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7571 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7572 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7573 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7574 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7575 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7576 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7577 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7578 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7579 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7580 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7581 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7582 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7583 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7584 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7585 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7589 /*---------------------------------------------------------------------------*\
\r
7591 * Front-end interface functions exported by XBoard.
\r
7592 * Functions appear in same order as prototypes in frontend.h.
\r
7594 \*---------------------------------------------------------------------------*/
\r
7598 static UINT prevChecked = 0;
\r
7599 static int prevPausing = 0;
\r
7602 if (pausing != prevPausing) {
\r
7603 prevPausing = pausing;
\r
7604 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7605 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7606 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7609 switch (gameMode) {
\r
7610 case BeginningOfGame:
\r
7611 if (appData.icsActive)
\r
7612 nowChecked = IDM_IcsClient;
\r
7613 else if (appData.noChessProgram)
\r
7614 nowChecked = IDM_EditGame;
\r
7616 nowChecked = IDM_MachineBlack;
\r
7618 case MachinePlaysBlack:
\r
7619 nowChecked = IDM_MachineBlack;
\r
7621 case MachinePlaysWhite:
\r
7622 nowChecked = IDM_MachineWhite;
\r
7624 case TwoMachinesPlay:
\r
7625 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7628 nowChecked = IDM_AnalysisMode;
\r
7631 nowChecked = IDM_AnalyzeFile;
\r
7634 nowChecked = IDM_EditGame;
\r
7636 case PlayFromGameFile:
\r
7637 nowChecked = IDM_LoadGame;
\r
7639 case EditPosition:
\r
7640 nowChecked = IDM_EditPosition;
\r
7643 nowChecked = IDM_Training;
\r
7645 case IcsPlayingWhite:
\r
7646 case IcsPlayingBlack:
\r
7647 case IcsObserving:
\r
7649 nowChecked = IDM_IcsClient;
\r
7656 if (prevChecked != 0)
\r
7657 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7658 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7659 if (nowChecked != 0)
\r
7660 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7661 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7663 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7664 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7665 MF_BYCOMMAND|MF_ENABLED);
\r
7667 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7668 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7671 prevChecked = nowChecked;
\r
7673 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7674 if (appData.icsActive) {
\r
7675 if (appData.icsEngineAnalyze) {
\r
7676 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7677 MF_BYCOMMAND|MF_CHECKED);
\r
7679 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7680 MF_BYCOMMAND|MF_UNCHECKED);
\r
7688 HMENU hmenu = GetMenu(hwndMain);
\r
7689 SetMenuEnables(hmenu, icsEnables);
\r
7690 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7691 MF_BYPOSITION|MF_ENABLED);
\r
7693 if (appData.zippyPlay) {
\r
7694 SetMenuEnables(hmenu, zippyEnables);
\r
7695 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7696 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7697 MF_BYCOMMAND|MF_ENABLED);
\r
7705 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7711 HMENU hmenu = GetMenu(hwndMain);
\r
7712 SetMenuEnables(hmenu, ncpEnables);
\r
7713 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7714 MF_BYPOSITION|MF_GRAYED);
\r
7715 DrawMenuBar(hwndMain);
\r
7721 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7725 SetTrainingModeOn()
\r
7728 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7729 for (i = 0; i < N_BUTTONS; i++) {
\r
7730 if (buttonDesc[i].hwnd != NULL)
\r
7731 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7736 VOID SetTrainingModeOff()
\r
7739 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7740 for (i = 0; i < N_BUTTONS; i++) {
\r
7741 if (buttonDesc[i].hwnd != NULL)
\r
7742 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7748 SetUserThinkingEnables()
\r
7750 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7754 SetMachineThinkingEnables()
\r
7756 HMENU hMenu = GetMenu(hwndMain);
\r
7757 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7759 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7761 if (gameMode == MachinePlaysBlack) {
\r
7762 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7763 } else if (gameMode == MachinePlaysWhite) {
\r
7764 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7765 } else if (gameMode == TwoMachinesPlay) {
\r
7766 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
7772 DisplayTitle(char *str)
\r
7774 char title[MSG_SIZ], *host;
\r
7775 if (str[0] != NULLCHAR) {
\r
7776 strcpy(title, str);
\r
7777 } else if (appData.icsActive) {
\r
7778 if (appData.icsCommPort[0] != NULLCHAR)
\r
7781 host = appData.icsHost;
\r
7782 sprintf(title, "%s: %s", szTitle, host);
\r
7783 } else if (appData.noChessProgram) {
\r
7784 strcpy(title, szTitle);
\r
7786 strcpy(title, szTitle);
\r
7787 strcat(title, ": ");
\r
7788 strcat(title, first.tidy);
\r
7790 SetWindowText(hwndMain, title);
\r
7795 DisplayMessage(char *str1, char *str2)
\r
7799 int remain = MESSAGE_TEXT_MAX - 1;
\r
7802 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7803 messageText[0] = NULLCHAR;
\r
7805 len = strlen(str1);
\r
7806 if (len > remain) len = remain;
\r
7807 strncpy(messageText, str1, len);
\r
7808 messageText[len] = NULLCHAR;
\r
7811 if (*str2 && remain >= 2) {
\r
7813 strcat(messageText, " ");
\r
7816 len = strlen(str2);
\r
7817 if (len > remain) len = remain;
\r
7818 strncat(messageText, str2, len);
\r
7820 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7822 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7826 hdc = GetDC(hwndMain);
\r
7827 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7828 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7829 &messageRect, messageText, strlen(messageText), NULL);
\r
7830 (void) SelectObject(hdc, oldFont);
\r
7831 (void) ReleaseDC(hwndMain, hdc);
\r
7835 DisplayError(char *str, int error)
\r
7837 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7843 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7844 NULL, error, LANG_NEUTRAL,
\r
7845 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7847 sprintf(buf, "%s:\n%s", str, buf2);
\r
7849 ErrorMap *em = errmap;
\r
7850 while (em->err != 0 && em->err != error) em++;
\r
7851 if (em->err != 0) {
\r
7852 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7854 sprintf(buf, "%s:\nError code %d", str, error);
\r
7859 ErrorPopUp("Error", buf);
\r
7864 DisplayMoveError(char *str)
\r
7866 fromX = fromY = -1;
\r
7867 ClearHighlights();
\r
7868 DrawPosition(FALSE, NULL);
\r
7869 if (appData.popupMoveErrors) {
\r
7870 ErrorPopUp("Error", str);
\r
7872 DisplayMessage(str, "");
\r
7873 moveErrorMessageUp = TRUE;
\r
7878 DisplayFatalError(char *str, int error, int exitStatus)
\r
7880 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7882 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7885 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7886 NULL, error, LANG_NEUTRAL,
\r
7887 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7889 sprintf(buf, "%s:\n%s", str, buf2);
\r
7891 ErrorMap *em = errmap;
\r
7892 while (em->err != 0 && em->err != error) em++;
\r
7893 if (em->err != 0) {
\r
7894 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7896 sprintf(buf, "%s:\nError code %d", str, error);
\r
7901 if (appData.debugMode) {
\r
7902 fprintf(debugFP, "%s: %s\n", label, str);
\r
7904 if (appData.popupExitMessage) {
\r
7905 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7906 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7908 ExitEvent(exitStatus);
\r
7913 DisplayInformation(char *str)
\r
7915 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7920 DisplayNote(char *str)
\r
7922 ErrorPopUp("Note", str);
\r
7927 char *title, *question, *replyPrefix;
\r
7932 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7934 static QuestionParams *qp;
\r
7935 char reply[MSG_SIZ];
\r
7938 switch (message) {
\r
7939 case WM_INITDIALOG:
\r
7940 qp = (QuestionParams *) lParam;
\r
7941 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7942 SetWindowText(hDlg, qp->title);
\r
7943 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7944 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7948 switch (LOWORD(wParam)) {
\r
7950 strcpy(reply, qp->replyPrefix);
\r
7951 if (*reply) strcat(reply, " ");
\r
7952 len = strlen(reply);
\r
7953 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7954 strcat(reply, "\n");
\r
7955 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7956 EndDialog(hDlg, TRUE);
\r
7957 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7960 EndDialog(hDlg, FALSE);
\r
7971 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7973 QuestionParams qp;
\r
7977 qp.question = question;
\r
7978 qp.replyPrefix = replyPrefix;
\r
7980 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7981 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7982 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7983 FreeProcInstance(lpProc);
\r
7986 /* [AS] Pick FRC position */
\r
7987 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7989 static int * lpIndexFRC;
\r
7995 case WM_INITDIALOG:
\r
7996 lpIndexFRC = (int *) lParam;
\r
7998 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8000 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8001 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8002 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8003 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8008 switch( LOWORD(wParam) ) {
\r
8010 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8011 EndDialog( hDlg, 0 );
\r
8012 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8015 EndDialog( hDlg, 1 );
\r
8017 case IDC_NFG_Edit:
\r
8018 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8019 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8021 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8024 case IDC_NFG_Random:
\r
8025 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8026 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8039 int index = appData.defaultFrcPosition;
\r
8040 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8042 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8044 if( result == 0 ) {
\r
8045 appData.defaultFrcPosition = index;
\r
8051 /* [AS] Game list options. Refactored by HGM */
\r
8053 HWND gameListOptionsDialog;
\r
8055 // low-level front-end: clear text edit / list widget
\r
8059 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8062 // low-level front-end: clear text edit / list widget
\r
8064 GLT_DeSelectList()
\r
8066 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8069 // low-level front-end: append line to text edit / list widget
\r
8071 GLT_AddToList( char *name )
\r
8074 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8078 // low-level front-end: get line from text edit / list widget
\r
8080 GLT_GetFromList( int index, char *name )
\r
8083 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8089 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8091 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8092 int idx2 = idx1 + delta;
\r
8093 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8095 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8098 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8099 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8100 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8101 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8105 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8109 case WM_INITDIALOG:
\r
8110 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8112 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8114 /* Initialize list */
\r
8115 GLT_TagsToList( lpUserGLT );
\r
8117 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8122 switch( LOWORD(wParam) ) {
\r
8125 EndDialog( hDlg, 0 );
\r
8128 EndDialog( hDlg, 1 );
\r
8131 case IDC_GLT_Default:
\r
8132 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8135 case IDC_GLT_Restore:
\r
8136 GLT_TagsToList( appData.gameListTags );
\r
8140 GLT_MoveSelection( hDlg, -1 );
\r
8143 case IDC_GLT_Down:
\r
8144 GLT_MoveSelection( hDlg, +1 );
\r
8154 int GameListOptions()
\r
8157 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8159 strcpy( lpUserGLT, appData.gameListTags );
\r
8161 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8163 if( result == 0 ) {
\r
8164 /* [AS] Memory leak here! */
\r
8165 appData.gameListTags = strdup( lpUserGLT );
\r
8172 DisplayIcsInteractionTitle(char *str)
\r
8174 char consoleTitle[MSG_SIZ];
\r
8176 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8177 SetWindowText(hwndConsole, consoleTitle);
\r
8181 DrawPosition(int fullRedraw, Board board)
\r
8183 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8186 void NotifyFrontendLogin()
\r
8189 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8195 fromX = fromY = -1;
\r
8196 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8197 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8198 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8199 dragInfo.lastpos = dragInfo.pos;
\r
8200 dragInfo.start.x = dragInfo.start.y = -1;
\r
8201 dragInfo.from = dragInfo.start;
\r
8203 DrawPosition(TRUE, NULL);
\r
8210 CommentPopUp(char *title, char *str)
\r
8212 HWND hwnd = GetActiveWindow();
\r
8213 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8215 SetActiveWindow(hwnd);
\r
8219 CommentPopDown(void)
\r
8221 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8222 if (commentDialog) {
\r
8223 ShowWindow(commentDialog, SW_HIDE);
\r
8225 commentUp = FALSE;
\r
8229 EditCommentPopUp(int index, char *title, char *str)
\r
8231 EitherCommentPopUp(index, title, str, TRUE);
\r
8238 MyPlaySound(&sounds[(int)SoundMove]);
\r
8241 VOID PlayIcsWinSound()
\r
8243 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8246 VOID PlayIcsLossSound()
\r
8248 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8251 VOID PlayIcsDrawSound()
\r
8253 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8256 VOID PlayIcsUnfinishedSound()
\r
8258 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8264 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8272 consoleEcho = TRUE;
\r
8273 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8274 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8275 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8284 consoleEcho = FALSE;
\r
8285 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8286 /* This works OK: set text and background both to the same color */
\r
8288 cf.crTextColor = COLOR_ECHOOFF;
\r
8289 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8290 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8293 /* No Raw()...? */
\r
8295 void Colorize(ColorClass cc, int continuation)
\r
8297 currentColorClass = cc;
\r
8298 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8299 consoleCF.crTextColor = textAttribs[cc].color;
\r
8300 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8301 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8307 static char buf[MSG_SIZ];
\r
8308 DWORD bufsiz = MSG_SIZ;
\r
8310 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8311 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8313 if (!GetUserName(buf, &bufsiz)) {
\r
8314 /*DisplayError("Error getting user name", GetLastError());*/
\r
8315 strcpy(buf, "User");
\r
8323 static char buf[MSG_SIZ];
\r
8324 DWORD bufsiz = MSG_SIZ;
\r
8326 if (!GetComputerName(buf, &bufsiz)) {
\r
8327 /*DisplayError("Error getting host name", GetLastError());*/
\r
8328 strcpy(buf, "Unknown");
\r
8335 ClockTimerRunning()
\r
8337 return clockTimerEvent != 0;
\r
8343 if (clockTimerEvent == 0) return FALSE;
\r
8344 KillTimer(hwndMain, clockTimerEvent);
\r
8345 clockTimerEvent = 0;
\r
8350 StartClockTimer(long millisec)
\r
8352 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8353 (UINT) millisec, NULL);
\r
8357 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8360 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8362 if(appData.noGUI) return;
\r
8363 hdc = GetDC(hwndMain);
\r
8364 if (!IsIconic(hwndMain)) {
\r
8365 DisplayAClock(hdc, timeRemaining, highlight,
\r
8366 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8368 if (highlight && iconCurrent == iconBlack) {
\r
8369 iconCurrent = iconWhite;
\r
8370 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8371 if (IsIconic(hwndMain)) {
\r
8372 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8375 (void) ReleaseDC(hwndMain, hdc);
\r
8377 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8381 DisplayBlackClock(long timeRemaining, int highlight)
\r
8384 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8386 if(appData.noGUI) return;
\r
8387 hdc = GetDC(hwndMain);
\r
8388 if (!IsIconic(hwndMain)) {
\r
8389 DisplayAClock(hdc, timeRemaining, highlight,
\r
8390 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8392 if (highlight && iconCurrent == iconWhite) {
\r
8393 iconCurrent = iconBlack;
\r
8394 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8395 if (IsIconic(hwndMain)) {
\r
8396 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8399 (void) ReleaseDC(hwndMain, hdc);
\r
8401 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8406 LoadGameTimerRunning()
\r
8408 return loadGameTimerEvent != 0;
\r
8412 StopLoadGameTimer()
\r
8414 if (loadGameTimerEvent == 0) return FALSE;
\r
8415 KillTimer(hwndMain, loadGameTimerEvent);
\r
8416 loadGameTimerEvent = 0;
\r
8421 StartLoadGameTimer(long millisec)
\r
8423 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8424 (UINT) millisec, NULL);
\r
8432 char fileTitle[MSG_SIZ];
\r
8434 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8435 f = OpenFileDialog(hwndMain, "a", defName,
\r
8436 appData.oldSaveStyle ? "gam" : "pgn",
\r
8438 "Save Game to File", NULL, fileTitle, NULL);
\r
8440 SaveGame(f, 0, "");
\r
8447 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8449 if (delayedTimerEvent != 0) {
\r
8450 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8451 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8453 KillTimer(hwndMain, delayedTimerEvent);
\r
8454 delayedTimerEvent = 0;
\r
8455 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8456 delayedTimerCallback();
\r
8458 delayedTimerCallback = cb;
\r
8459 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8460 (UINT) millisec, NULL);
\r
8463 DelayedEventCallback
\r
8466 if (delayedTimerEvent) {
\r
8467 return delayedTimerCallback;
\r
8474 CancelDelayedEvent()
\r
8476 if (delayedTimerEvent) {
\r
8477 KillTimer(hwndMain, delayedTimerEvent);
\r
8478 delayedTimerEvent = 0;
\r
8482 DWORD GetWin32Priority(int nice)
\r
8483 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8485 REALTIME_PRIORITY_CLASS 0x00000100
\r
8486 HIGH_PRIORITY_CLASS 0x00000080
\r
8487 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8488 NORMAL_PRIORITY_CLASS 0x00000020
\r
8489 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8490 IDLE_PRIORITY_CLASS 0x00000040
\r
8492 if (nice < -15) return 0x00000080;
\r
8493 if (nice < 0) return 0x00008000;
\r
8494 if (nice == 0) return 0x00000020;
\r
8495 if (nice < 15) return 0x00004000;
\r
8496 return 0x00000040;
\r
8499 /* Start a child process running the given program.
\r
8500 The process's standard output can be read from "from", and its
\r
8501 standard input can be written to "to".
\r
8502 Exit with fatal error if anything goes wrong.
\r
8503 Returns an opaque pointer that can be used to destroy the process
\r
8507 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8509 #define BUFSIZE 4096
\r
8511 HANDLE hChildStdinRd, hChildStdinWr,
\r
8512 hChildStdoutRd, hChildStdoutWr;
\r
8513 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8514 SECURITY_ATTRIBUTES saAttr;
\r
8516 PROCESS_INFORMATION piProcInfo;
\r
8517 STARTUPINFO siStartInfo;
\r
8519 char buf[MSG_SIZ];
\r
8522 if (appData.debugMode) {
\r
8523 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8528 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8529 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8530 saAttr.bInheritHandle = TRUE;
\r
8531 saAttr.lpSecurityDescriptor = NULL;
\r
8534 * The steps for redirecting child's STDOUT:
\r
8535 * 1. Create anonymous pipe to be STDOUT for child.
\r
8536 * 2. Create a noninheritable duplicate of read handle,
\r
8537 * and close the inheritable read handle.
\r
8540 /* Create a pipe for the child's STDOUT. */
\r
8541 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8542 return GetLastError();
\r
8545 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8546 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8547 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8548 FALSE, /* not inherited */
\r
8549 DUPLICATE_SAME_ACCESS);
\r
8551 return GetLastError();
\r
8553 CloseHandle(hChildStdoutRd);
\r
8556 * The steps for redirecting child's STDIN:
\r
8557 * 1. Create anonymous pipe to be STDIN for child.
\r
8558 * 2. Create a noninheritable duplicate of write handle,
\r
8559 * and close the inheritable write handle.
\r
8562 /* Create a pipe for the child's STDIN. */
\r
8563 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8564 return GetLastError();
\r
8567 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8568 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8569 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8570 FALSE, /* not inherited */
\r
8571 DUPLICATE_SAME_ACCESS);
\r
8573 return GetLastError();
\r
8575 CloseHandle(hChildStdinWr);
\r
8577 /* Arrange to (1) look in dir for the child .exe file, and
\r
8578 * (2) have dir be the child's working directory. Interpret
\r
8579 * dir relative to the directory WinBoard loaded from. */
\r
8580 GetCurrentDirectory(MSG_SIZ, buf);
\r
8581 SetCurrentDirectory(installDir);
\r
8582 SetCurrentDirectory(dir);
\r
8584 /* Now create the child process. */
\r
8586 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8587 siStartInfo.lpReserved = NULL;
\r
8588 siStartInfo.lpDesktop = NULL;
\r
8589 siStartInfo.lpTitle = NULL;
\r
8590 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8591 siStartInfo.cbReserved2 = 0;
\r
8592 siStartInfo.lpReserved2 = NULL;
\r
8593 siStartInfo.hStdInput = hChildStdinRd;
\r
8594 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8595 siStartInfo.hStdError = hChildStdoutWr;
\r
8597 fSuccess = CreateProcess(NULL,
\r
8598 cmdLine, /* command line */
\r
8599 NULL, /* process security attributes */
\r
8600 NULL, /* primary thread security attrs */
\r
8601 TRUE, /* handles are inherited */
\r
8602 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8603 NULL, /* use parent's environment */
\r
8605 &siStartInfo, /* STARTUPINFO pointer */
\r
8606 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8608 err = GetLastError();
\r
8609 SetCurrentDirectory(buf); /* return to prev directory */
\r
8614 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8615 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8616 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8619 /* Close the handles we don't need in the parent */
\r
8620 CloseHandle(piProcInfo.hThread);
\r
8621 CloseHandle(hChildStdinRd);
\r
8622 CloseHandle(hChildStdoutWr);
\r
8624 /* Prepare return value */
\r
8625 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8626 cp->kind = CPReal;
\r
8627 cp->hProcess = piProcInfo.hProcess;
\r
8628 cp->pid = piProcInfo.dwProcessId;
\r
8629 cp->hFrom = hChildStdoutRdDup;
\r
8630 cp->hTo = hChildStdinWrDup;
\r
8632 *pr = (void *) cp;
\r
8634 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8635 2000 where engines sometimes don't see the initial command(s)
\r
8636 from WinBoard and hang. I don't understand how that can happen,
\r
8637 but the Sleep is harmless, so I've put it in. Others have also
\r
8638 reported what may be the same problem, so hopefully this will fix
\r
8639 it for them too. */
\r
8647 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8649 ChildProc *cp; int result;
\r
8651 cp = (ChildProc *) pr;
\r
8652 if (cp == NULL) return;
\r
8654 switch (cp->kind) {
\r
8656 /* TerminateProcess is considered harmful, so... */
\r
8657 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8658 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8659 /* The following doesn't work because the chess program
\r
8660 doesn't "have the same console" as WinBoard. Maybe
\r
8661 we could arrange for this even though neither WinBoard
\r
8662 nor the chess program uses a console for stdio? */
\r
8663 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8665 /* [AS] Special termination modes for misbehaving programs... */
\r
8666 if( signal == 9 ) {
\r
8667 result = TerminateProcess( cp->hProcess, 0 );
\r
8669 if ( appData.debugMode) {
\r
8670 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8673 else if( signal == 10 ) {
\r
8674 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8676 if( dw != WAIT_OBJECT_0 ) {
\r
8677 result = TerminateProcess( cp->hProcess, 0 );
\r
8679 if ( appData.debugMode) {
\r
8680 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8686 CloseHandle(cp->hProcess);
\r
8690 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8694 closesocket(cp->sock);
\r
8699 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8700 closesocket(cp->sock);
\r
8701 closesocket(cp->sock2);
\r
8709 InterruptChildProcess(ProcRef pr)
\r
8713 cp = (ChildProc *) pr;
\r
8714 if (cp == NULL) return;
\r
8715 switch (cp->kind) {
\r
8717 /* The following doesn't work because the chess program
\r
8718 doesn't "have the same console" as WinBoard. Maybe
\r
8719 we could arrange for this even though neither WinBoard
\r
8720 nor the chess program uses a console for stdio */
\r
8721 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8726 /* Can't interrupt */
\r
8730 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8737 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8739 char cmdLine[MSG_SIZ];
\r
8741 if (port[0] == NULLCHAR) {
\r
8742 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8744 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8746 return StartChildProcess(cmdLine, "", pr);
\r
8750 /* Code to open TCP sockets */
\r
8753 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8758 struct sockaddr_in sa, mysa;
\r
8759 struct hostent FAR *hp;
\r
8760 unsigned short uport;
\r
8761 WORD wVersionRequested;
\r
8764 /* Initialize socket DLL */
\r
8765 wVersionRequested = MAKEWORD(1, 1);
\r
8766 err = WSAStartup(wVersionRequested, &wsaData);
\r
8767 if (err != 0) return err;
\r
8770 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8771 err = WSAGetLastError();
\r
8776 /* Bind local address using (mostly) don't-care values.
\r
8778 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8779 mysa.sin_family = AF_INET;
\r
8780 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8781 uport = (unsigned short) 0;
\r
8782 mysa.sin_port = htons(uport);
\r
8783 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8784 == SOCKET_ERROR) {
\r
8785 err = WSAGetLastError();
\r
8790 /* Resolve remote host name */
\r
8791 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8792 if (!(hp = gethostbyname(host))) {
\r
8793 unsigned int b0, b1, b2, b3;
\r
8795 err = WSAGetLastError();
\r
8797 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8798 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8799 hp->h_addrtype = AF_INET;
\r
8801 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8802 hp->h_addr_list[0] = (char *) malloc(4);
\r
8803 hp->h_addr_list[0][0] = (char) b0;
\r
8804 hp->h_addr_list[0][1] = (char) b1;
\r
8805 hp->h_addr_list[0][2] = (char) b2;
\r
8806 hp->h_addr_list[0][3] = (char) b3;
\r
8812 sa.sin_family = hp->h_addrtype;
\r
8813 uport = (unsigned short) atoi(port);
\r
8814 sa.sin_port = htons(uport);
\r
8815 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8817 /* Make connection */
\r
8818 if (connect(s, (struct sockaddr *) &sa,
\r
8819 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8820 err = WSAGetLastError();
\r
8825 /* Prepare return value */
\r
8826 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8827 cp->kind = CPSock;
\r
8829 *pr = (ProcRef *) cp;
\r
8835 OpenCommPort(char *name, ProcRef *pr)
\r
8840 char fullname[MSG_SIZ];
\r
8842 if (*name != '\\')
\r
8843 sprintf(fullname, "\\\\.\\%s", name);
\r
8845 strcpy(fullname, name);
\r
8847 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8848 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8849 if (h == (HANDLE) -1) {
\r
8850 return GetLastError();
\r
8854 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8856 /* Accumulate characters until a 100ms pause, then parse */
\r
8857 ct.ReadIntervalTimeout = 100;
\r
8858 ct.ReadTotalTimeoutMultiplier = 0;
\r
8859 ct.ReadTotalTimeoutConstant = 0;
\r
8860 ct.WriteTotalTimeoutMultiplier = 0;
\r
8861 ct.WriteTotalTimeoutConstant = 0;
\r
8862 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8864 /* Prepare return value */
\r
8865 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8866 cp->kind = CPComm;
\r
8869 *pr = (ProcRef *) cp;
\r
8875 OpenLoopback(ProcRef *pr)
\r
8877 DisplayFatalError("Not implemented", 0, 1);
\r
8883 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8888 struct sockaddr_in sa, mysa;
\r
8889 struct hostent FAR *hp;
\r
8890 unsigned short uport;
\r
8891 WORD wVersionRequested;
\r
8894 char stderrPortStr[MSG_SIZ];
\r
8896 /* Initialize socket DLL */
\r
8897 wVersionRequested = MAKEWORD(1, 1);
\r
8898 err = WSAStartup(wVersionRequested, &wsaData);
\r
8899 if (err != 0) return err;
\r
8901 /* Resolve remote host name */
\r
8902 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8903 if (!(hp = gethostbyname(host))) {
\r
8904 unsigned int b0, b1, b2, b3;
\r
8906 err = WSAGetLastError();
\r
8908 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8909 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8910 hp->h_addrtype = AF_INET;
\r
8912 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8913 hp->h_addr_list[0] = (char *) malloc(4);
\r
8914 hp->h_addr_list[0][0] = (char) b0;
\r
8915 hp->h_addr_list[0][1] = (char) b1;
\r
8916 hp->h_addr_list[0][2] = (char) b2;
\r
8917 hp->h_addr_list[0][3] = (char) b3;
\r
8923 sa.sin_family = hp->h_addrtype;
\r
8924 uport = (unsigned short) 514;
\r
8925 sa.sin_port = htons(uport);
\r
8926 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8928 /* Bind local socket to unused "privileged" port address
\r
8930 s = INVALID_SOCKET;
\r
8931 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8932 mysa.sin_family = AF_INET;
\r
8933 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8934 for (fromPort = 1023;; fromPort--) {
\r
8935 if (fromPort < 0) {
\r
8937 return WSAEADDRINUSE;
\r
8939 if (s == INVALID_SOCKET) {
\r
8940 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8941 err = WSAGetLastError();
\r
8946 uport = (unsigned short) fromPort;
\r
8947 mysa.sin_port = htons(uport);
\r
8948 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8949 == SOCKET_ERROR) {
\r
8950 err = WSAGetLastError();
\r
8951 if (err == WSAEADDRINUSE) continue;
\r
8955 if (connect(s, (struct sockaddr *) &sa,
\r
8956 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8957 err = WSAGetLastError();
\r
8958 if (err == WSAEADDRINUSE) {
\r
8969 /* Bind stderr local socket to unused "privileged" port address
\r
8971 s2 = INVALID_SOCKET;
\r
8972 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8973 mysa.sin_family = AF_INET;
\r
8974 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8975 for (fromPort = 1023;; fromPort--) {
\r
8976 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8977 if (fromPort < 0) {
\r
8978 (void) closesocket(s);
\r
8980 return WSAEADDRINUSE;
\r
8982 if (s2 == INVALID_SOCKET) {
\r
8983 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8984 err = WSAGetLastError();
\r
8990 uport = (unsigned short) fromPort;
\r
8991 mysa.sin_port = htons(uport);
\r
8992 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8993 == SOCKET_ERROR) {
\r
8994 err = WSAGetLastError();
\r
8995 if (err == WSAEADDRINUSE) continue;
\r
8996 (void) closesocket(s);
\r
9000 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9001 err = WSAGetLastError();
\r
9002 if (err == WSAEADDRINUSE) {
\r
9004 s2 = INVALID_SOCKET;
\r
9007 (void) closesocket(s);
\r
9008 (void) closesocket(s2);
\r
9014 prevStderrPort = fromPort; // remember port used
\r
9015 sprintf(stderrPortStr, "%d", fromPort);
\r
9017 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9018 err = WSAGetLastError();
\r
9019 (void) closesocket(s);
\r
9020 (void) closesocket(s2);
\r
9025 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9026 err = WSAGetLastError();
\r
9027 (void) closesocket(s);
\r
9028 (void) closesocket(s2);
\r
9032 if (*user == NULLCHAR) user = UserName();
\r
9033 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9034 err = WSAGetLastError();
\r
9035 (void) closesocket(s);
\r
9036 (void) closesocket(s2);
\r
9040 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9041 err = WSAGetLastError();
\r
9042 (void) closesocket(s);
\r
9043 (void) closesocket(s2);
\r
9048 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9049 err = WSAGetLastError();
\r
9050 (void) closesocket(s);
\r
9051 (void) closesocket(s2);
\r
9055 (void) closesocket(s2); /* Stop listening */
\r
9057 /* Prepare return value */
\r
9058 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9059 cp->kind = CPRcmd;
\r
9062 *pr = (ProcRef *) cp;
\r
9069 AddInputSource(ProcRef pr, int lineByLine,
\r
9070 InputCallback func, VOIDSTAR closure)
\r
9072 InputSource *is, *is2 = NULL;
\r
9073 ChildProc *cp = (ChildProc *) pr;
\r
9075 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9076 is->lineByLine = lineByLine;
\r
9078 is->closure = closure;
\r
9079 is->second = NULL;
\r
9080 is->next = is->buf;
\r
9081 if (pr == NoProc) {
\r
9082 is->kind = CPReal;
\r
9083 consoleInputSource = is;
\r
9085 is->kind = cp->kind;
\r
9087 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9088 we create all threads suspended so that the is->hThread variable can be
\r
9089 safely assigned, then let the threads start with ResumeThread.
\r
9091 switch (cp->kind) {
\r
9093 is->hFile = cp->hFrom;
\r
9094 cp->hFrom = NULL; /* now owned by InputThread */
\r
9096 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9097 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9101 is->hFile = cp->hFrom;
\r
9102 cp->hFrom = NULL; /* now owned by InputThread */
\r
9104 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9105 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9109 is->sock = cp->sock;
\r
9111 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9112 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9116 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9118 is->sock = cp->sock;
\r
9120 is2->sock = cp->sock2;
\r
9121 is2->second = is2;
\r
9123 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9124 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9126 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9127 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9131 if( is->hThread != NULL ) {
\r
9132 ResumeThread( is->hThread );
\r
9135 if( is2 != NULL && is2->hThread != NULL ) {
\r
9136 ResumeThread( is2->hThread );
\r
9140 return (InputSourceRef) is;
\r
9144 RemoveInputSource(InputSourceRef isr)
\r
9148 is = (InputSource *) isr;
\r
9149 is->hThread = NULL; /* tell thread to stop */
\r
9150 CloseHandle(is->hThread);
\r
9151 if (is->second != NULL) {
\r
9152 is->second->hThread = NULL;
\r
9153 CloseHandle(is->second->hThread);
\r
9157 int no_wrap(char *message, int count)
\r
9159 ConsoleOutput(message, count, FALSE);
\r
9164 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9167 int outCount = SOCKET_ERROR;
\r
9168 ChildProc *cp = (ChildProc *) pr;
\r
9169 static OVERLAPPED ovl;
\r
9170 static int line = 0;
\r
9174 if (appData.noJoin || !appData.useInternalWrap)
\r
9175 return no_wrap(message, count);
\r
9178 int width = get_term_width();
\r
9179 int len = wrap(NULL, message, count, width, &line);
\r
9180 char *msg = malloc(len);
\r
9184 return no_wrap(message, count);
\r
9187 dbgchk = wrap(msg, message, count, width, &line);
\r
9188 if (dbgchk != len && appData.debugMode)
\r
9189 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9190 ConsoleOutput(msg, len, FALSE);
\r
9197 if (ovl.hEvent == NULL) {
\r
9198 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9200 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9202 switch (cp->kind) {
\r
9205 outCount = send(cp->sock, message, count, 0);
\r
9206 if (outCount == SOCKET_ERROR) {
\r
9207 *outError = WSAGetLastError();
\r
9209 *outError = NO_ERROR;
\r
9214 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9215 &dOutCount, NULL)) {
\r
9216 *outError = NO_ERROR;
\r
9217 outCount = (int) dOutCount;
\r
9219 *outError = GetLastError();
\r
9224 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9225 &dOutCount, &ovl);
\r
9226 if (*outError == NO_ERROR) {
\r
9227 outCount = (int) dOutCount;
\r
9235 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9238 /* Ignore delay, not implemented for WinBoard */
\r
9239 return OutputToProcess(pr, message, count, outError);
\r
9244 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9245 char *buf, int count, int error)
\r
9247 DisplayFatalError("Not implemented", 0, 1);
\r
9250 /* see wgamelist.c for Game List functions */
\r
9251 /* see wedittags.c for Edit Tags functions */
\r
9258 char buf[MSG_SIZ];
\r
9261 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9262 f = fopen(buf, "r");
\r
9264 ProcessICSInitScript(f);
\r
9272 StartAnalysisClock()
\r
9274 if (analysisTimerEvent) return;
\r
9275 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9276 (UINT) 2000, NULL);
\r
9280 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9282 highlightInfo.sq[0].x = fromX;
\r
9283 highlightInfo.sq[0].y = fromY;
\r
9284 highlightInfo.sq[1].x = toX;
\r
9285 highlightInfo.sq[1].y = toY;
\r
9291 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9292 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9296 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9298 premoveHighlightInfo.sq[0].x = fromX;
\r
9299 premoveHighlightInfo.sq[0].y = fromY;
\r
9300 premoveHighlightInfo.sq[1].x = toX;
\r
9301 premoveHighlightInfo.sq[1].y = toY;
\r
9305 ClearPremoveHighlights()
\r
9307 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9308 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9312 ShutDownFrontEnd()
\r
9314 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9315 DeleteClipboardTempFiles();
\r
9321 if (IsIconic(hwndMain))
\r
9322 ShowWindow(hwndMain, SW_RESTORE);
\r
9324 SetActiveWindow(hwndMain);
\r
9328 * Prototypes for animation support routines
\r
9330 static void ScreenSquare(int column, int row, POINT * pt);
\r
9331 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9332 POINT frames[], int * nFrames);
\r
9336 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9337 { // [HGM] atomic: animate blast wave
\r
9339 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9340 explodeInfo.fromX = fromX;
\r
9341 explodeInfo.fromY = fromY;
\r
9342 explodeInfo.toX = toX;
\r
9343 explodeInfo.toY = toY;
\r
9344 for(i=1; i<nFrames; i++) {
\r
9345 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9346 DrawPosition(FALSE, NULL);
\r
9347 Sleep(appData.animSpeed);
\r
9349 explodeInfo.radius = 0;
\r
9350 DrawPosition(TRUE, NULL);
\r
9356 AnimateMove(board, fromX, fromY, toX, toY)
\r
9363 ChessSquare piece;
\r
9364 POINT start, finish, mid;
\r
9365 POINT frames[kFactor * 2 + 1];
\r
9368 if (!appData.animate) return;
\r
9369 if (doingSizing) return;
\r
9370 if (fromY < 0 || fromX < 0) return;
\r
9371 piece = board[fromY][fromX];
\r
9372 if (piece >= EmptySquare) return;
\r
9374 ScreenSquare(fromX, fromY, &start);
\r
9375 ScreenSquare(toX, toY, &finish);
\r
9377 /* All pieces except knights move in straight line */
\r
9378 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9379 mid.x = start.x + (finish.x - start.x) / 2;
\r
9380 mid.y = start.y + (finish.y - start.y) / 2;
\r
9382 /* Knight: make diagonal movement then straight */
\r
9383 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9384 mid.x = start.x + (finish.x - start.x) / 2;
\r
9388 mid.y = start.y + (finish.y - start.y) / 2;
\r
9392 /* Don't use as many frames for very short moves */
\r
9393 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9394 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9396 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9398 animInfo.from.x = fromX;
\r
9399 animInfo.from.y = fromY;
\r
9400 animInfo.to.x = toX;
\r
9401 animInfo.to.y = toY;
\r
9402 animInfo.lastpos = start;
\r
9403 animInfo.piece = piece;
\r
9404 for (n = 0; n < nFrames; n++) {
\r
9405 animInfo.pos = frames[n];
\r
9406 DrawPosition(FALSE, NULL);
\r
9407 animInfo.lastpos = animInfo.pos;
\r
9408 Sleep(appData.animSpeed);
\r
9410 animInfo.pos = finish;
\r
9411 DrawPosition(FALSE, NULL);
\r
9412 animInfo.piece = EmptySquare;
\r
9413 if(gameInfo.variant == VariantAtomic &&
\r
9414 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9415 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9418 /* Convert board position to corner of screen rect and color */
\r
9421 ScreenSquare(column, row, pt)
\r
9422 int column; int row; POINT * pt;
\r
9425 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9426 pt->y = lineGap + row * (squareSize + lineGap);
\r
9428 pt->x = lineGap + column * (squareSize + lineGap);
\r
9429 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9433 /* Generate a series of frame coords from start->mid->finish.
\r
9434 The movement rate doubles until the half way point is
\r
9435 reached, then halves back down to the final destination,
\r
9436 which gives a nice slow in/out effect. The algorithmn
\r
9437 may seem to generate too many intermediates for short
\r
9438 moves, but remember that the purpose is to attract the
\r
9439 viewers attention to the piece about to be moved and
\r
9440 then to where it ends up. Too few frames would be less
\r
9444 Tween(start, mid, finish, factor, frames, nFrames)
\r
9445 POINT * start; POINT * mid;
\r
9446 POINT * finish; int factor;
\r
9447 POINT frames[]; int * nFrames;
\r
9449 int n, fraction = 1, count = 0;
\r
9451 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9452 for (n = 0; n < factor; n++)
\r
9454 for (n = 0; n < factor; n++) {
\r
9455 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9456 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9458 fraction = fraction / 2;
\r
9462 frames[count] = *mid;
\r
9465 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9467 for (n = 0; n < factor; n++) {
\r
9468 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9469 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9471 fraction = fraction * 2;
\r
9477 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9479 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9481 EvalGraphSet( first, last, current, pvInfoList );
\r