2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
234 int cliWidth, cliHeight;
\r
237 SizeInfo sizeInfo[] =
\r
239 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
240 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
241 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
242 { "petite", 33, 1, 1, 1, 0, 0 },
\r
243 { "slim", 37, 2, 1, 0, 0, 0 },
\r
244 { "small", 40, 2, 1, 0, 0, 0 },
\r
245 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
246 { "middling", 49, 2, 0, 0, 0, 0 },
\r
247 { "average", 54, 2, 0, 0, 0, 0 },
\r
248 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
249 { "medium", 64, 3, 0, 0, 0, 0 },
\r
250 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
251 { "large", 80, 3, 0, 0, 0, 0 },
\r
252 { "big", 87, 3, 0, 0, 0, 0 },
\r
253 { "huge", 95, 3, 0, 0, 0, 0 },
\r
254 { "giant", 108, 3, 0, 0, 0, 0 },
\r
255 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
256 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
257 { NULL, 0, 0, 0, 0, 0, 0 }
\r
260 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
261 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
263 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
264 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
265 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
266 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
267 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
268 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
269 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
270 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
271 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
272 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
273 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
274 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
275 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
276 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
277 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
278 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
279 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
280 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
283 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
292 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
293 #define N_BUTTONS 5
\r
295 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
297 {"<<", IDM_ToStart, NULL, NULL},
\r
298 {"<", IDM_Backward, NULL, NULL},
\r
299 {"P", IDM_Pause, NULL, NULL},
\r
300 {">", IDM_Forward, NULL, NULL},
\r
301 {">>", IDM_ToEnd, NULL, NULL},
\r
304 int tinyLayout = 0, smallLayout = 0;
\r
305 #define MENU_BAR_ITEMS 7
\r
306 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
307 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
308 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
312 MySound sounds[(int)NSoundClasses];
\r
313 MyTextAttribs textAttribs[(int)NColorClasses];
\r
315 MyColorizeAttribs colorizeAttribs[] = {
\r
316 { (COLORREF)0, 0, "Shout Text" },
\r
317 { (COLORREF)0, 0, "SShout/CShout" },
\r
318 { (COLORREF)0, 0, "Channel 1 Text" },
\r
319 { (COLORREF)0, 0, "Channel Text" },
\r
320 { (COLORREF)0, 0, "Kibitz Text" },
\r
321 { (COLORREF)0, 0, "Tell Text" },
\r
322 { (COLORREF)0, 0, "Challenge Text" },
\r
323 { (COLORREF)0, 0, "Request Text" },
\r
324 { (COLORREF)0, 0, "Seek Text" },
\r
325 { (COLORREF)0, 0, "Normal Text" },
\r
326 { (COLORREF)0, 0, "None" }
\r
331 static char *commentTitle;
\r
332 static char *commentText;
\r
333 static int commentIndex;
\r
334 static Boolean editComment = FALSE;
\r
337 char errorTitle[MSG_SIZ];
\r
338 char errorMessage[2*MSG_SIZ];
\r
339 HWND errorDialog = NULL;
\r
340 BOOLEAN moveErrorMessageUp = FALSE;
\r
341 BOOLEAN consoleEcho = TRUE;
\r
342 CHARFORMAT consoleCF;
\r
343 COLORREF consoleBackgroundColor;
\r
345 char *programVersion;
\r
351 typedef int CPKind;
\r
360 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
363 #define INPUT_SOURCE_BUF_SIZE 4096
\r
365 typedef struct _InputSource {
\r
372 char buf[INPUT_SOURCE_BUF_SIZE];
\r
376 InputCallback func;
\r
377 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
381 InputSource *consoleInputSource;
\r
386 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
387 VOID ConsoleCreate();
\r
389 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
390 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
391 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
392 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
394 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
395 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
396 void ParseIcsTextMenu(char *icsTextMenuString);
\r
397 VOID PopUpMoveDialog(char firstchar);
\r
398 VOID PopUpNameDialog(char firstchar);
\r
399 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
403 int GameListOptions();
\r
405 int dummy; // [HGM] for obsolete args
\r
407 HWND hwndMain = NULL; /* root window*/
\r
408 HWND hwndConsole = NULL;
\r
409 HWND commentDialog = NULL;
\r
410 HWND moveHistoryDialog = NULL;
\r
411 HWND evalGraphDialog = NULL;
\r
412 HWND engineOutputDialog = NULL;
\r
413 HWND gameListDialog = NULL;
\r
414 HWND editTagsDialog = NULL;
\r
416 int commentUp = FALSE;
\r
418 WindowPlacement wpMain;
\r
419 WindowPlacement wpConsole;
\r
420 WindowPlacement wpComment;
\r
421 WindowPlacement wpMoveHistory;
\r
422 WindowPlacement wpEvalGraph;
\r
423 WindowPlacement wpEngineOutput;
\r
424 WindowPlacement wpGameList;
\r
425 WindowPlacement wpTags;
\r
427 VOID EngineOptionsPopup(); // [HGM] settings
\r
429 VOID GothicPopUp(char *title, VariantClass variant);
\r
431 * Setting "frozen" should disable all user input other than deleting
\r
432 * the window. We do this while engines are initializing themselves.
\r
434 static int frozen = 0;
\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
441 if (frozen) return;
\r
443 hmenu = GetMenu(hwndMain);
\r
444 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
445 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
447 DrawMenuBar(hwndMain);
\r
450 /* Undo a FreezeUI */
\r
456 if (!frozen) return;
\r
458 hmenu = GetMenu(hwndMain);
\r
459 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
460 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
462 DrawMenuBar(hwndMain);
\r
465 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
467 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
473 #define JAWS_ALT_INTERCEPT
\r
474 #define JAWS_KB_NAVIGATION
\r
475 #define JAWS_MENU_ITEMS
\r
476 #define JAWS_SILENCE
\r
477 #define JAWS_REPLAY
\r
479 #define JAWS_COPYRIGHT
\r
480 #define JAWS_DELETE(X) X
\r
481 #define SAYMACHINEMOVE()
\r
485 /*---------------------------------------------------------------------------*\
\r
489 \*---------------------------------------------------------------------------*/
\r
492 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
493 LPSTR lpCmdLine, int nCmdShow)
\r
496 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
497 // INITCOMMONCONTROLSEX ex;
\r
501 LoadLibrary("RICHED32.DLL");
\r
502 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
504 if (!InitApplication(hInstance)) {
\r
507 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
513 // InitCommonControlsEx(&ex);
\r
514 InitCommonControls();
\r
516 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
517 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
518 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
520 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
522 while (GetMessage(&msg, /* message structure */
\r
523 NULL, /* handle of window receiving the message */
\r
524 0, /* lowest message to examine */
\r
525 0)) /* highest message to examine */
\r
528 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
529 // [HGM] navigate: switch between all windows with tab
\r
530 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
531 int i, currentElement = 0;
\r
533 // first determine what element of the chain we come from (if any)
\r
534 if(appData.icsActive) {
\r
535 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
536 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
538 if(engineOutputDialog && EngineOutputIsUp()) {
\r
539 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
540 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
542 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
543 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
545 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
546 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
547 if(msg.hwnd == e1) currentElement = 2; else
\r
548 if(msg.hwnd == e2) currentElement = 3; else
\r
549 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
550 if(msg.hwnd == mh) currentElement = 4; else
\r
551 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
552 if(msg.hwnd == hText) currentElement = 5; else
\r
553 if(msg.hwnd == hInput) currentElement = 6; else
\r
554 for (i = 0; i < N_BUTTONS; i++) {
\r
555 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
558 // determine where to go to
\r
559 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
561 currentElement = (currentElement + direction) % 7;
\r
562 switch(currentElement) {
\r
564 h = hwndMain; break; // passing this case always makes the loop exit
\r
566 h = buttonDesc[0].hwnd; break; // could be NULL
\r
568 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
571 if(!EngineOutputIsUp()) continue;
\r
574 if(!MoveHistoryIsUp()) continue;
\r
576 // case 6: // input to eval graph does not seem to get here!
\r
577 // if(!EvalGraphIsUp()) continue;
\r
578 // h = evalGraphDialog; break;
\r
580 if(!appData.icsActive) continue;
\r
584 if(!appData.icsActive) continue;
\r
590 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
591 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
594 continue; // this message now has been processed
\r
598 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
599 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
600 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
601 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
602 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
603 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
604 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
605 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
606 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
607 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
608 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
609 for(i=0; i<MAX_CHAT; i++)
\r
610 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
613 if(done) continue; // [HGM] chat: end patch
\r
614 TranslateMessage(&msg); /* Translates virtual key codes */
\r
615 DispatchMessage(&msg); /* Dispatches message to window */
\r
620 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
623 /*---------------------------------------------------------------------------*\
\r
625 * Initialization functions
\r
627 \*---------------------------------------------------------------------------*/
\r
631 { // update user logo if necessary
\r
632 static char oldUserName[MSG_SIZ], *curName;
\r
634 if(appData.autoLogo) {
\r
635 curName = UserName();
\r
636 if(strcmp(curName, oldUserName)) {
\r
637 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
638 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
639 strcpy(oldUserName, curName);
\r
645 InitApplication(HINSTANCE hInstance)
\r
649 /* Fill in window class structure with parameters that describe the */
\r
652 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
653 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
654 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
655 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
656 wc.hInstance = hInstance; /* Owner of this class */
\r
657 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
658 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
659 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
660 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
661 wc.lpszClassName = szAppName; /* Name to register as */
\r
663 /* Register the window class and return success/failure code. */
\r
664 if (!RegisterClass(&wc)) return FALSE;
\r
666 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
667 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
669 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
670 wc.hInstance = hInstance;
\r
671 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
672 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
673 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
674 wc.lpszMenuName = NULL;
\r
675 wc.lpszClassName = szConsoleName;
\r
677 if (!RegisterClass(&wc)) return FALSE;
\r
682 /* Set by InitInstance, used by EnsureOnScreen */
\r
683 int screenHeight, screenWidth;
\r
686 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
688 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
689 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
690 if (*x > screenWidth - 32) *x = 0;
\r
691 if (*y > screenHeight - 32) *y = 0;
\r
692 if (*x < minX) *x = minX;
\r
693 if (*y < minY) *y = minY;
\r
697 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
699 HWND hwnd; /* Main window handle. */
\r
701 WINDOWPLACEMENT wp;
\r
704 hInst = hInstance; /* Store instance handle in our global variable */
\r
705 programName = szAppName;
\r
707 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
708 *filepart = NULLCHAR;
\r
710 GetCurrentDirectory(MSG_SIZ, installDir);
\r
712 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
713 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
714 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
715 /* xboard, and older WinBoards, controlled the move sound with the
\r
716 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
717 always turn the option on (so that the backend will call us),
\r
718 then let the user turn the sound off by setting it to silence if
\r
719 desired. To accommodate old winboard.ini files saved by old
\r
720 versions of WinBoard, we also turn off the sound if the option
\r
721 was initially set to false. [HGM] taken out of InitAppData */
\r
722 if (!appData.ringBellAfterMoves) {
\r
723 sounds[(int)SoundMove].name = strdup("");
\r
724 appData.ringBellAfterMoves = TRUE;
\r
726 if (appData.debugMode) {
\r
727 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
728 setbuf(debugFP, NULL);
\r
733 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
734 // InitEngineUCI( installDir, &second );
\r
736 /* Create a main window for this application instance. */
\r
737 hwnd = CreateWindow(szAppName, szTitle,
\r
738 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
739 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
740 NULL, NULL, hInstance, NULL);
\r
743 /* If window could not be created, return "failure" */
\r
748 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
749 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
750 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
752 if (first.programLogo == NULL && appData.debugMode) {
\r
753 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
755 } else if(appData.autoLogo) {
\r
756 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
758 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
759 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
763 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
764 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
766 if (second.programLogo == NULL && appData.debugMode) {
\r
767 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
769 } else if(appData.autoLogo) {
\r
771 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
772 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
773 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
775 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
776 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
777 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
783 iconWhite = LoadIcon(hInstance, "icon_white");
\r
784 iconBlack = LoadIcon(hInstance, "icon_black");
\r
785 iconCurrent = iconWhite;
\r
786 InitDrawingColors();
\r
787 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
788 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
789 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
790 /* Compute window size for each board size, and use the largest
\r
791 size that fits on this screen as the default. */
\r
792 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
793 if (boardSize == (BoardSize)-1 &&
\r
794 winH <= screenHeight
\r
795 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
796 && winW <= screenWidth) {
\r
797 boardSize = (BoardSize)ibs;
\r
801 InitDrawingSizes(boardSize, 0);
\r
803 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
805 /* [AS] Load textures if specified */
\r
806 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
808 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
809 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
810 liteBackTextureMode = appData.liteBackTextureMode;
\r
812 if (liteBackTexture == NULL && appData.debugMode) {
\r
813 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
817 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
818 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
819 darkBackTextureMode = appData.darkBackTextureMode;
\r
821 if (darkBackTexture == NULL && appData.debugMode) {
\r
822 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
826 mysrandom( (unsigned) time(NULL) );
\r
828 /* [AS] Restore layout */
\r
829 if( wpMoveHistory.visible ) {
\r
830 MoveHistoryPopUp();
\r
833 if( wpEvalGraph.visible ) {
\r
837 if( wpEngineOutput.visible ) {
\r
838 EngineOutputPopUp();
\r
841 /* Make the window visible; update its client area; and return "success" */
\r
842 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
843 wp.length = sizeof(WINDOWPLACEMENT);
\r
845 wp.showCmd = nCmdShow;
\r
846 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
847 wp.rcNormalPosition.left = wpMain.x;
\r
848 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
849 wp.rcNormalPosition.top = wpMain.y;
\r
850 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
851 SetWindowPlacement(hwndMain, &wp);
\r
853 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
855 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
856 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
860 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
861 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
863 ShowWindow(hwndConsole, nCmdShow);
\r
864 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
865 char buf[MSG_SIZ], *p = buf, *q;
\r
866 strcpy(buf, appData.chatBoxes);
\r
868 q = strchr(p, ';');
\r
870 if(*p) ChatPopUp(p);
\r
873 SetActiveWindow(hwndConsole);
\r
875 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
876 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
885 HMENU hmenu = GetMenu(hwndMain);
\r
887 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
888 MF_BYCOMMAND|((appData.icsActive &&
\r
889 *appData.icsCommPort != NULLCHAR) ?
\r
890 MF_ENABLED : MF_GRAYED));
\r
891 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
892 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
893 MF_CHECKED : MF_UNCHECKED));
\r
896 //---------------------------------------------------------------------------------------------------------
\r
898 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
899 #define XBOARD FALSE
\r
901 #define OPTCHAR "/"
\r
902 #define SEPCHAR "="
\r
906 // front-end part of option handling
\r
909 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
911 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
912 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
915 lf->lfEscapement = 0;
\r
916 lf->lfOrientation = 0;
\r
917 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
918 lf->lfItalic = mfp->italic;
\r
919 lf->lfUnderline = mfp->underline;
\r
920 lf->lfStrikeOut = mfp->strikeout;
\r
921 lf->lfCharSet = mfp->charset;
\r
922 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
923 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
924 lf->lfQuality = DEFAULT_QUALITY;
\r
925 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
926 strcpy(lf->lfFaceName, mfp->faceName);
\r
930 CreateFontInMF(MyFont *mf)
\r
932 LFfromMFP(&mf->lf, &mf->mfp);
\r
933 if (mf->hf) DeleteObject(mf->hf);
\r
934 mf->hf = CreateFontIndirect(&mf->lf);
\r
937 // [HGM] This platform-dependent table provides the location for storing the color info
\r
939 colorVariable[] = {
\r
944 &highlightSquareColor,
\r
945 &premoveHighlightColor,
\r
947 &consoleBackgroundColor,
\r
948 &appData.fontForeColorWhite,
\r
949 &appData.fontBackColorWhite,
\r
950 &appData.fontForeColorBlack,
\r
951 &appData.fontBackColorBlack,
\r
952 &appData.evalHistColorWhite,
\r
953 &appData.evalHistColorBlack,
\r
954 &appData.highlightArrowColor,
\r
957 /* Command line font name parser. NULL name means do nothing.
\r
958 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
959 For backward compatibility, syntax without the colon is also
\r
960 accepted, but font names with digits in them won't work in that case.
\r
963 ParseFontName(char *name, MyFontParams *mfp)
\r
966 if (name == NULL) return;
\r
968 q = strchr(p, ':');
\r
970 if (q - p >= sizeof(mfp->faceName))
\r
971 ExitArgError("Font name too long:", name);
\r
972 memcpy(mfp->faceName, p, q - p);
\r
973 mfp->faceName[q - p] = NULLCHAR;
\r
977 while (*p && !isdigit(*p)) {
\r
979 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
980 ExitArgError("Font name too long:", name);
\r
982 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
985 if (!*p) ExitArgError("Font point size missing:", name);
\r
986 mfp->pointSize = (float) atof(p);
\r
987 mfp->bold = (strchr(p, 'b') != NULL);
\r
988 mfp->italic = (strchr(p, 'i') != NULL);
\r
989 mfp->underline = (strchr(p, 'u') != NULL);
\r
990 mfp->strikeout = (strchr(p, 's') != NULL);
\r
991 mfp->charset = DEFAULT_CHARSET;
\r
992 q = strchr(p, 'c');
\r
994 mfp->charset = (BYTE) atoi(q+1);
\r
998 ParseFont(char *name, int number)
\r
999 { // wrapper to shield back-end from 'font'
\r
1000 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1005 { // in WB we have a 2D array of fonts; this initializes their description
\r
1007 /* Point font array elements to structures and
\r
1008 parse default font names */
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 font[j][i] = &fontRec[j][i];
\r
1012 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1019 { // here we create the actual fonts from the selected descriptions
\r
1021 for (i=0; i<NUM_FONTS; i++) {
\r
1022 for (j=0; j<NUM_SIZES; j++) {
\r
1023 CreateFontInMF(font[j][i]);
\r
1027 /* Color name parser.
\r
1028 X version accepts X color names, but this one
\r
1029 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1031 ParseColorName(char *name)
\r
1033 int red, green, blue, count;
\r
1034 char buf[MSG_SIZ];
\r
1036 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1038 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1039 &red, &green, &blue);
\r
1042 sprintf(buf, "Can't parse color name %s", name);
\r
1043 DisplayError(buf, 0);
\r
1044 return RGB(0, 0, 0);
\r
1046 return PALETTERGB(red, green, blue);
\r
1050 ParseColor(int n, char *name)
\r
1051 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1052 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1056 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1058 char *e = argValue;
\r
1062 if (*e == 'b') eff |= CFE_BOLD;
\r
1063 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1064 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1065 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1066 else if (*e == '#' || isdigit(*e)) break;
\r
1070 *color = ParseColorName(e);
\r
1074 ParseTextAttribs(ColorClass cc, char *s)
\r
1075 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1076 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1077 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1081 ParseBoardSize(void *addr, char *name)
\r
1082 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1083 BoardSize bs = SizeTiny;
\r
1084 while (sizeInfo[bs].name != NULL) {
\r
1085 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1086 *(BoardSize *)addr = bs;
\r
1091 ExitArgError("Unrecognized board size value", name);
\r
1096 { // [HGM] import name from appData first
\r
1099 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1100 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1101 textAttribs[cc].sound.data = NULL;
\r
1102 MyLoadSound(&textAttribs[cc].sound);
\r
1104 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1105 textAttribs[cc].sound.name = strdup("");
\r
1106 textAttribs[cc].sound.data = NULL;
\r
1108 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1109 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1110 sounds[sc].data = NULL;
\r
1111 MyLoadSound(&sounds[sc]);
\r
1116 SetCommPortDefaults()
\r
1118 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1119 dcb.DCBlength = sizeof(DCB);
\r
1120 dcb.BaudRate = 9600;
\r
1121 dcb.fBinary = TRUE;
\r
1122 dcb.fParity = FALSE;
\r
1123 dcb.fOutxCtsFlow = FALSE;
\r
1124 dcb.fOutxDsrFlow = FALSE;
\r
1125 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1126 dcb.fDsrSensitivity = FALSE;
\r
1127 dcb.fTXContinueOnXoff = TRUE;
\r
1128 dcb.fOutX = FALSE;
\r
1130 dcb.fNull = FALSE;
\r
1131 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1132 dcb.fAbortOnError = FALSE;
\r
1134 dcb.Parity = SPACEPARITY;
\r
1135 dcb.StopBits = ONESTOPBIT;
\r
1138 // [HGM] args: these three cases taken out to stay in front-end
\r
1140 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1141 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1142 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1143 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1145 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1146 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1147 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1148 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1149 ad->argName, mfp->faceName, mfp->pointSize,
\r
1150 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1151 mfp->bold ? "b" : "",
\r
1152 mfp->italic ? "i" : "",
\r
1153 mfp->underline ? "u" : "",
\r
1154 mfp->strikeout ? "s" : "",
\r
1155 (int)mfp->charset);
\r
1161 { // [HGM] copy the names from the internal WB variables to appData
\r
1164 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1165 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1166 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1167 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1171 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1172 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1173 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1174 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1175 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1176 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1177 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1178 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1179 (ta->effects) ? " " : "",
\r
1180 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1184 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1185 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1186 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1187 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1188 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1192 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1193 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1194 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1198 ParseCommPortSettings(char *s)
\r
1199 { // wrapper to keep dcb from back-end
\r
1200 ParseCommSettings(s, &dcb);
\r
1205 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1206 GetActualPlacement(hwndMain, &wpMain);
\r
1207 GetActualPlacement(hwndConsole, &wpConsole);
\r
1208 GetActualPlacement(commentDialog, &wpComment);
\r
1209 GetActualPlacement(editTagsDialog, &wpTags);
\r
1210 GetActualPlacement(gameListDialog, &wpGameList);
\r
1211 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1212 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1213 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1217 PrintCommPortSettings(FILE *f, char *name)
\r
1218 { // wrapper to shield back-end from DCB
\r
1219 PrintCommSettings(f, name, &dcb);
\r
1223 MySearchPath(char *installDir, char *name, char *fullname)
\r
1225 char *dummy, buf[MSG_SIZ];
\r
1226 if(name[0] == '%' && strchr(name+1, '%')) { // [HGM] recognize %*% as environment variable
\r
1227 strcpy(buf, name+1);
\r
1228 *strchr(buf, '%') = 0;
\r
1229 installDir = getenv(buf);
\r
1230 sprintf(fullname, "%s\\%s", installDir, strchr(name+1, '%')+1);
\r
1231 return strlen(fullname);
\r
1233 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1237 MyGetFullPathName(char *name, char *fullname)
\r
1240 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1245 { // [HGM] args: allows testing if main window is realized from back-end
\r
1246 return hwndMain != NULL;
\r
1250 PopUpStartupDialog()
\r
1254 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1255 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1256 FreeProcInstance(lpProc);
\r
1259 /*---------------------------------------------------------------------------*\
\r
1261 * GDI board drawing routines
\r
1263 \*---------------------------------------------------------------------------*/
\r
1265 /* [AS] Draw square using background texture */
\r
1266 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1271 return; /* Should never happen! */
\r
1274 SetGraphicsMode( dst, GM_ADVANCED );
\r
1281 /* X reflection */
\r
1286 x.eDx = (FLOAT) dw + dx - 1;
\r
1289 SetWorldTransform( dst, &x );
\r
1292 /* Y reflection */
\r
1298 x.eDy = (FLOAT) dh + dy - 1;
\r
1300 SetWorldTransform( dst, &x );
\r
1308 x.eDx = (FLOAT) dx;
\r
1309 x.eDy = (FLOAT) dy;
\r
1312 SetWorldTransform( dst, &x );
\r
1316 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1324 SetWorldTransform( dst, &x );
\r
1326 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1329 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1331 PM_WP = (int) WhitePawn,
\r
1332 PM_WN = (int) WhiteKnight,
\r
1333 PM_WB = (int) WhiteBishop,
\r
1334 PM_WR = (int) WhiteRook,
\r
1335 PM_WQ = (int) WhiteQueen,
\r
1336 PM_WF = (int) WhiteFerz,
\r
1337 PM_WW = (int) WhiteWazir,
\r
1338 PM_WE = (int) WhiteAlfil,
\r
1339 PM_WM = (int) WhiteMan,
\r
1340 PM_WO = (int) WhiteCannon,
\r
1341 PM_WU = (int) WhiteUnicorn,
\r
1342 PM_WH = (int) WhiteNightrider,
\r
1343 PM_WA = (int) WhiteAngel,
\r
1344 PM_WC = (int) WhiteMarshall,
\r
1345 PM_WAB = (int) WhiteCardinal,
\r
1346 PM_WD = (int) WhiteDragon,
\r
1347 PM_WL = (int) WhiteLance,
\r
1348 PM_WS = (int) WhiteCobra,
\r
1349 PM_WV = (int) WhiteFalcon,
\r
1350 PM_WSG = (int) WhiteSilver,
\r
1351 PM_WG = (int) WhiteGrasshopper,
\r
1352 PM_WK = (int) WhiteKing,
\r
1353 PM_BP = (int) BlackPawn,
\r
1354 PM_BN = (int) BlackKnight,
\r
1355 PM_BB = (int) BlackBishop,
\r
1356 PM_BR = (int) BlackRook,
\r
1357 PM_BQ = (int) BlackQueen,
\r
1358 PM_BF = (int) BlackFerz,
\r
1359 PM_BW = (int) BlackWazir,
\r
1360 PM_BE = (int) BlackAlfil,
\r
1361 PM_BM = (int) BlackMan,
\r
1362 PM_BO = (int) BlackCannon,
\r
1363 PM_BU = (int) BlackUnicorn,
\r
1364 PM_BH = (int) BlackNightrider,
\r
1365 PM_BA = (int) BlackAngel,
\r
1366 PM_BC = (int) BlackMarshall,
\r
1367 PM_BG = (int) BlackGrasshopper,
\r
1368 PM_BAB = (int) BlackCardinal,
\r
1369 PM_BD = (int) BlackDragon,
\r
1370 PM_BL = (int) BlackLance,
\r
1371 PM_BS = (int) BlackCobra,
\r
1372 PM_BV = (int) BlackFalcon,
\r
1373 PM_BSG = (int) BlackSilver,
\r
1374 PM_BK = (int) BlackKing
\r
1377 static HFONT hPieceFont = NULL;
\r
1378 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1379 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1380 static int fontBitmapSquareSize = 0;
\r
1381 static char pieceToFontChar[(int) EmptySquare] =
\r
1382 { 'p', 'n', 'b', 'r', 'q',
\r
1383 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1384 'k', 'o', 'm', 'v', 't', 'w',
\r
1385 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1388 extern BOOL SetCharTable( char *table, const char * map );
\r
1389 /* [HGM] moved to backend.c */
\r
1391 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1394 BYTE r1 = GetRValue( color );
\r
1395 BYTE g1 = GetGValue( color );
\r
1396 BYTE b1 = GetBValue( color );
\r
1402 /* Create a uniform background first */
\r
1403 hbrush = CreateSolidBrush( color );
\r
1404 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1405 FillRect( hdc, &rc, hbrush );
\r
1406 DeleteObject( hbrush );
\r
1409 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1410 int steps = squareSize / 2;
\r
1413 for( i=0; i<steps; i++ ) {
\r
1414 BYTE r = r1 - (r1-r2) * i / steps;
\r
1415 BYTE g = g1 - (g1-g2) * i / steps;
\r
1416 BYTE b = b1 - (b1-b2) * i / steps;
\r
1418 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1419 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1420 FillRect( hdc, &rc, hbrush );
\r
1421 DeleteObject(hbrush);
\r
1424 else if( mode == 2 ) {
\r
1425 /* Diagonal gradient, good more or less for every piece */
\r
1426 POINT triangle[3];
\r
1427 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1428 HBRUSH hbrush_old;
\r
1429 int steps = squareSize;
\r
1432 triangle[0].x = squareSize - steps;
\r
1433 triangle[0].y = squareSize;
\r
1434 triangle[1].x = squareSize;
\r
1435 triangle[1].y = squareSize;
\r
1436 triangle[2].x = squareSize;
\r
1437 triangle[2].y = squareSize - steps;
\r
1439 for( i=0; i<steps; i++ ) {
\r
1440 BYTE r = r1 - (r1-r2) * i / steps;
\r
1441 BYTE g = g1 - (g1-g2) * i / steps;
\r
1442 BYTE b = b1 - (b1-b2) * i / steps;
\r
1444 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1445 hbrush_old = SelectObject( hdc, hbrush );
\r
1446 Polygon( hdc, triangle, 3 );
\r
1447 SelectObject( hdc, hbrush_old );
\r
1448 DeleteObject(hbrush);
\r
1453 SelectObject( hdc, hpen );
\r
1458 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1459 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1460 piece: follow the steps as explained below.
\r
1462 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1466 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1470 int backColor = whitePieceColor;
\r
1471 int foreColor = blackPieceColor;
\r
1473 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1474 backColor = appData.fontBackColorWhite;
\r
1475 foreColor = appData.fontForeColorWhite;
\r
1477 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1478 backColor = appData.fontBackColorBlack;
\r
1479 foreColor = appData.fontForeColorBlack;
\r
1483 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1485 hbm_old = SelectObject( hdc, hbm );
\r
1489 rc.right = squareSize;
\r
1490 rc.bottom = squareSize;
\r
1492 /* Step 1: background is now black */
\r
1493 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1495 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1497 pt.x = (squareSize - sz.cx) / 2;
\r
1498 pt.y = (squareSize - sz.cy) / 2;
\r
1500 SetBkMode( hdc, TRANSPARENT );
\r
1501 SetTextColor( hdc, chroma );
\r
1502 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1503 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1505 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1506 /* Step 3: the area outside the piece is filled with white */
\r
1507 // FloodFill( hdc, 0, 0, chroma );
\r
1508 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1509 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1510 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1511 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1512 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1514 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1515 but if the start point is not inside the piece we're lost!
\r
1516 There should be a better way to do this... if we could create a region or path
\r
1517 from the fill operation we would be fine for example.
\r
1519 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1520 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1522 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1523 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1524 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1526 SelectObject( dc2, bm2 );
\r
1527 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1528 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1529 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1530 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1531 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1534 DeleteObject( bm2 );
\r
1537 SetTextColor( hdc, 0 );
\r
1539 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1540 draw the piece again in black for safety.
\r
1542 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1544 SelectObject( hdc, hbm_old );
\r
1546 if( hPieceMask[index] != NULL ) {
\r
1547 DeleteObject( hPieceMask[index] );
\r
1550 hPieceMask[index] = hbm;
\r
1553 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1555 SelectObject( hdc, hbm );
\r
1558 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1559 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1560 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1562 SelectObject( dc1, hPieceMask[index] );
\r
1563 SelectObject( dc2, bm2 );
\r
1564 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1565 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1568 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1569 the piece background and deletes (makes transparent) the rest.
\r
1570 Thanks to that mask, we are free to paint the background with the greates
\r
1571 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1572 We use this, to make gradients and give the pieces a "roundish" look.
\r
1574 SetPieceBackground( hdc, backColor, 2 );
\r
1575 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1579 DeleteObject( bm2 );
\r
1582 SetTextColor( hdc, foreColor );
\r
1583 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1585 SelectObject( hdc, hbm_old );
\r
1587 if( hPieceFace[index] != NULL ) {
\r
1588 DeleteObject( hPieceFace[index] );
\r
1591 hPieceFace[index] = hbm;
\r
1594 static int TranslatePieceToFontPiece( int piece )
\r
1624 case BlackMarshall:
\r
1628 case BlackNightrider:
\r
1634 case BlackUnicorn:
\r
1638 case BlackGrasshopper:
\r
1650 case BlackCardinal:
\r
1657 case WhiteMarshall:
\r
1661 case WhiteNightrider:
\r
1667 case WhiteUnicorn:
\r
1671 case WhiteGrasshopper:
\r
1683 case WhiteCardinal:
\r
1692 void CreatePiecesFromFont()
\r
1695 HDC hdc_window = NULL;
\r
1701 if( fontBitmapSquareSize < 0 ) {
\r
1702 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1706 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1707 fontBitmapSquareSize = -1;
\r
1711 if( fontBitmapSquareSize != squareSize ) {
\r
1712 hdc_window = GetDC( hwndMain );
\r
1713 hdc = CreateCompatibleDC( hdc_window );
\r
1715 if( hPieceFont != NULL ) {
\r
1716 DeleteObject( hPieceFont );
\r
1719 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1720 hPieceMask[i] = NULL;
\r
1721 hPieceFace[i] = NULL;
\r
1727 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1728 fontHeight = appData.fontPieceSize;
\r
1731 fontHeight = (fontHeight * squareSize) / 100;
\r
1733 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1735 lf.lfEscapement = 0;
\r
1736 lf.lfOrientation = 0;
\r
1737 lf.lfWeight = FW_NORMAL;
\r
1739 lf.lfUnderline = 0;
\r
1740 lf.lfStrikeOut = 0;
\r
1741 lf.lfCharSet = DEFAULT_CHARSET;
\r
1742 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1743 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1744 lf.lfQuality = PROOF_QUALITY;
\r
1745 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1746 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1747 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1749 hPieceFont = CreateFontIndirect( &lf );
\r
1751 if( hPieceFont == NULL ) {
\r
1752 fontBitmapSquareSize = -2;
\r
1755 /* Setup font-to-piece character table */
\r
1756 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1757 /* No (or wrong) global settings, try to detect the font */
\r
1758 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1760 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1762 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1763 /* DiagramTT* family */
\r
1764 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1766 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1767 /* Fairy symbols */
\r
1768 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1770 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1771 /* Good Companion (Some characters get warped as literal :-( */
\r
1772 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1773 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1774 SetCharTable(pieceToFontChar, s);
\r
1777 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1778 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1782 /* Create bitmaps */
\r
1783 hfont_old = SelectObject( hdc, hPieceFont );
\r
1784 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1785 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1786 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1788 SelectObject( hdc, hfont_old );
\r
1790 fontBitmapSquareSize = squareSize;
\r
1794 if( hdc != NULL ) {
\r
1798 if( hdc_window != NULL ) {
\r
1799 ReleaseDC( hwndMain, hdc_window );
\r
1804 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1808 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1809 if (gameInfo.event &&
\r
1810 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1811 strcmp(name, "k80s") == 0) {
\r
1812 strcpy(name, "tim");
\r
1814 return LoadBitmap(hinst, name);
\r
1818 /* Insert a color into the program's logical palette
\r
1819 structure. This code assumes the given color is
\r
1820 the result of the RGB or PALETTERGB macro, and it
\r
1821 knows how those macros work (which is documented).
\r
1824 InsertInPalette(COLORREF color)
\r
1826 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1828 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1829 DisplayFatalError("Too many colors", 0, 1);
\r
1830 pLogPal->palNumEntries--;
\r
1834 pe->peFlags = (char) 0;
\r
1835 pe->peRed = (char) (0xFF & color);
\r
1836 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1837 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1843 InitDrawingColors()
\r
1845 if (pLogPal == NULL) {
\r
1846 /* Allocate enough memory for a logical palette with
\r
1847 * PALETTESIZE entries and set the size and version fields
\r
1848 * of the logical palette structure.
\r
1850 pLogPal = (NPLOGPALETTE)
\r
1851 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1852 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1853 pLogPal->palVersion = 0x300;
\r
1855 pLogPal->palNumEntries = 0;
\r
1857 InsertInPalette(lightSquareColor);
\r
1858 InsertInPalette(darkSquareColor);
\r
1859 InsertInPalette(whitePieceColor);
\r
1860 InsertInPalette(blackPieceColor);
\r
1861 InsertInPalette(highlightSquareColor);
\r
1862 InsertInPalette(premoveHighlightColor);
\r
1864 /* create a logical color palette according the information
\r
1865 * in the LOGPALETTE structure.
\r
1867 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1869 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1870 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1871 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1872 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1873 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1874 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1875 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1876 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1877 /* [AS] Force rendering of the font-based pieces */
\r
1878 if( fontBitmapSquareSize > 0 ) {
\r
1879 fontBitmapSquareSize = 0;
\r
1885 BoardWidth(int boardSize, int n)
\r
1886 { /* [HGM] argument n added to allow different width and height */
\r
1887 int lineGap = sizeInfo[boardSize].lineGap;
\r
1889 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1890 lineGap = appData.overrideLineGap;
\r
1893 return (n + 1) * lineGap +
\r
1894 n * sizeInfo[boardSize].squareSize;
\r
1897 /* Respond to board resize by dragging edge */
\r
1899 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1901 BoardSize newSize = NUM_SIZES - 1;
\r
1902 static int recurse = 0;
\r
1903 if (IsIconic(hwndMain)) return;
\r
1904 if (recurse > 0) return;
\r
1906 while (newSize > 0) {
\r
1907 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1908 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1909 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1912 boardSize = newSize;
\r
1913 InitDrawingSizes(boardSize, flags);
\r
1918 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1921 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1923 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1924 ChessSquare piece;
\r
1925 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1927 SIZE clockSize, messageSize;
\r
1929 char buf[MSG_SIZ];
\r
1931 HMENU hmenu = GetMenu(hwndMain);
\r
1932 RECT crect, wrect, oldRect;
\r
1934 LOGBRUSH logbrush;
\r
1936 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1937 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1939 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1940 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1942 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1943 oldRect.top = wpMain.y;
\r
1944 oldRect.right = wpMain.x + wpMain.width;
\r
1945 oldRect.bottom = wpMain.y + wpMain.height;
\r
1947 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1948 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1949 squareSize = sizeInfo[boardSize].squareSize;
\r
1950 lineGap = sizeInfo[boardSize].lineGap;
\r
1951 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1953 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1954 lineGap = appData.overrideLineGap;
\r
1957 if (tinyLayout != oldTinyLayout) {
\r
1958 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1960 style &= ~WS_SYSMENU;
\r
1961 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1962 "&Minimize\tCtrl+F4");
\r
1964 style |= WS_SYSMENU;
\r
1965 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1967 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1969 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1970 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1971 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1973 DrawMenuBar(hwndMain);
\r
1976 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1977 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1979 /* Get text area sizes */
\r
1980 hdc = GetDC(hwndMain);
\r
1981 if (appData.clockMode) {
\r
1982 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1984 sprintf(buf, "White");
\r
1986 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1987 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1988 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1989 str = "We only care about the height here";
\r
1990 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1991 SelectObject(hdc, oldFont);
\r
1992 ReleaseDC(hwndMain, hdc);
\r
1994 /* Compute where everything goes */
\r
1995 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1996 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1997 logoHeight = 2*clockSize.cy;
\r
1998 leftLogoRect.left = OUTER_MARGIN;
\r
1999 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2000 leftLogoRect.top = OUTER_MARGIN;
\r
2001 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2003 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2004 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2005 rightLogoRect.top = OUTER_MARGIN;
\r
2006 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2009 whiteRect.left = leftLogoRect.right;
\r
2010 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2011 whiteRect.top = OUTER_MARGIN;
\r
2012 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2014 blackRect.right = rightLogoRect.left;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.top = whiteRect.top;
\r
2017 blackRect.bottom = whiteRect.bottom;
\r
2019 whiteRect.left = OUTER_MARGIN;
\r
2020 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2021 whiteRect.top = OUTER_MARGIN;
\r
2022 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2024 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2025 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2026 blackRect.top = whiteRect.top;
\r
2027 blackRect.bottom = whiteRect.bottom;
\r
2029 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2032 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2033 if (appData.showButtonBar) {
\r
2034 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2035 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2037 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2039 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2040 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2042 boardRect.left = OUTER_MARGIN;
\r
2043 boardRect.right = boardRect.left + boardWidth;
\r
2044 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2045 boardRect.bottom = boardRect.top + boardHeight;
\r
2047 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2048 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2049 oldBoardSize = boardSize;
\r
2050 oldTinyLayout = tinyLayout;
\r
2051 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2052 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2053 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2054 winW *= 1 + twoBoards;
\r
2055 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2056 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2057 wpMain.height = winH; // without disturbing window attachments
\r
2058 GetWindowRect(hwndMain, &wrect);
\r
2059 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2060 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2062 // [HGM] placement: let attached windows follow size change.
\r
2063 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2064 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2065 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2066 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2067 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2069 /* compensate if menu bar wrapped */
\r
2070 GetClientRect(hwndMain, &crect);
\r
2071 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2072 wpMain.height += offby;
\r
2074 case WMSZ_TOPLEFT:
\r
2075 SetWindowPos(hwndMain, NULL,
\r
2076 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2077 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2080 case WMSZ_TOPRIGHT:
\r
2082 SetWindowPos(hwndMain, NULL,
\r
2083 wrect.left, wrect.bottom - wpMain.height,
\r
2084 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2087 case WMSZ_BOTTOMLEFT:
\r
2089 SetWindowPos(hwndMain, NULL,
\r
2090 wrect.right - wpMain.width, wrect.top,
\r
2091 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2094 case WMSZ_BOTTOMRIGHT:
\r
2098 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2099 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2104 for (i = 0; i < N_BUTTONS; i++) {
\r
2105 if (buttonDesc[i].hwnd != NULL) {
\r
2106 DestroyWindow(buttonDesc[i].hwnd);
\r
2107 buttonDesc[i].hwnd = NULL;
\r
2109 if (appData.showButtonBar) {
\r
2110 buttonDesc[i].hwnd =
\r
2111 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2112 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2113 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2114 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2115 (HMENU) buttonDesc[i].id,
\r
2116 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2118 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2119 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2120 MAKELPARAM(FALSE, 0));
\r
2122 if (buttonDesc[i].id == IDM_Pause)
\r
2123 hwndPause = buttonDesc[i].hwnd;
\r
2124 buttonDesc[i].wndproc = (WNDPROC)
\r
2125 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2128 if (gridPen != NULL) DeleteObject(gridPen);
\r
2129 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2130 if (premovePen != NULL) DeleteObject(premovePen);
\r
2131 if (lineGap != 0) {
\r
2132 logbrush.lbStyle = BS_SOLID;
\r
2133 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2135 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2136 lineGap, &logbrush, 0, NULL);
\r
2137 logbrush.lbColor = highlightSquareColor;
\r
2139 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2140 lineGap, &logbrush, 0, NULL);
\r
2142 logbrush.lbColor = premoveHighlightColor;
\r
2144 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2145 lineGap, &logbrush, 0, NULL);
\r
2147 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2148 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2149 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2150 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2151 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2152 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2153 BOARD_WIDTH * (squareSize + lineGap);
\r
2154 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2156 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2157 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2158 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2159 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2160 lineGap / 2 + (i * (squareSize + lineGap));
\r
2161 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2162 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2163 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2167 /* [HGM] Licensing requirement */
\r
2169 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2172 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2174 GothicPopUp( "", VariantNormal);
\r
2177 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2179 /* Load piece bitmaps for this board size */
\r
2180 for (i=0; i<=2; i++) {
\r
2181 for (piece = WhitePawn;
\r
2182 (int) piece < (int) BlackPawn;
\r
2183 piece = (ChessSquare) ((int) piece + 1)) {
\r
2184 if (pieceBitmap[i][piece] != NULL)
\r
2185 DeleteObject(pieceBitmap[i][piece]);
\r
2189 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2190 // Orthodox Chess pieces
\r
2191 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2192 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2193 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2194 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2195 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2196 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2197 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2198 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2199 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2200 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2201 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2202 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2203 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2204 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2205 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2206 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2207 // in Shogi, Hijack the unused Queen for Lance
\r
2208 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2209 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2210 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2212 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2217 if(squareSize <= 72 && squareSize >= 33) {
\r
2218 /* A & C are available in most sizes now */
\r
2219 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2220 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2221 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2222 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2223 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2224 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2225 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2226 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2227 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2228 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2229 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2232 } else { // Smirf-like
\r
2233 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2237 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2238 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2241 } else { // WinBoard standard
\r
2242 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2249 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2250 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2259 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2271 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2272 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2273 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2281 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2282 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2285 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2286 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2287 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2288 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2289 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2290 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2291 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2292 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2293 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2295 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2296 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2297 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2298 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2299 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2300 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2301 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2302 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2303 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2304 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2305 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2306 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2309 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2310 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2311 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2312 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2313 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2314 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2315 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2316 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2317 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2318 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2319 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2320 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2321 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2322 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2323 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2327 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2328 /* special Shogi support in this size */
\r
2329 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2330 for (piece = WhitePawn;
\r
2331 (int) piece < (int) BlackPawn;
\r
2332 piece = (ChessSquare) ((int) piece + 1)) {
\r
2333 if (pieceBitmap[i][piece] != NULL)
\r
2334 DeleteObject(pieceBitmap[i][piece]);
\r
2337 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2342 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2344 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2345 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2346 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2347 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2348 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2349 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2350 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2351 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2356 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2358 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2359 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2360 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2361 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2362 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2363 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2364 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2365 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2370 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2372 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2373 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2374 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2375 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2376 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2377 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2378 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2384 PieceBitmap(ChessSquare p, int kind)
\r
2386 if ((int) p >= (int) BlackPawn)
\r
2387 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2389 return pieceBitmap[kind][(int) p];
\r
2392 /***************************************************************/
\r
2394 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2395 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2397 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2398 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2402 SquareToPos(int row, int column, int * x, int * y)
\r
2405 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2406 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2408 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2409 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2414 DrawCoordsOnDC(HDC hdc)
\r
2416 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2417 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2418 char str[2] = { NULLCHAR, NULLCHAR };
\r
2419 int oldMode, oldAlign, x, y, start, i;
\r
2423 if (!appData.showCoords)
\r
2426 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2428 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2429 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2430 oldAlign = GetTextAlign(hdc);
\r
2431 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2433 y = boardRect.top + lineGap;
\r
2434 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2436 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2437 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2438 str[0] = files[start + i];
\r
2439 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2440 y += squareSize + lineGap;
\r
2443 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2445 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2446 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2447 str[0] = ranks[start + i];
\r
2448 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2449 x += squareSize + lineGap;
\r
2452 SelectObject(hdc, oldBrush);
\r
2453 SetBkMode(hdc, oldMode);
\r
2454 SetTextAlign(hdc, oldAlign);
\r
2455 SelectObject(hdc, oldFont);
\r
2459 DrawGridOnDC(HDC hdc)
\r
2463 if (lineGap != 0) {
\r
2464 oldPen = SelectObject(hdc, gridPen);
\r
2465 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2466 SelectObject(hdc, oldPen);
\r
2470 #define HIGHLIGHT_PEN 0
\r
2471 #define PREMOVE_PEN 1
\r
2474 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2477 HPEN oldPen, hPen;
\r
2478 if (lineGap == 0) return;
\r
2480 x1 = boardRect.left +
\r
2481 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2482 y1 = boardRect.top +
\r
2483 lineGap/2 + y * (squareSize + lineGap);
\r
2485 x1 = boardRect.left +
\r
2486 lineGap/2 + x * (squareSize + lineGap);
\r
2487 y1 = boardRect.top +
\r
2488 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2490 hPen = pen ? premovePen : highlightPen;
\r
2491 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2492 MoveToEx(hdc, x1, y1, NULL);
\r
2493 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2494 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2495 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2496 LineTo(hdc, x1, y1);
\r
2497 SelectObject(hdc, oldPen);
\r
2501 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2504 for (i=0; i<2; i++) {
\r
2505 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2506 DrawHighlightOnDC(hdc, TRUE,
\r
2507 h->sq[i].x, h->sq[i].y,
\r
2512 /* Note: sqcolor is used only in monoMode */
\r
2513 /* Note that this code is largely duplicated in woptions.c,
\r
2514 function DrawSampleSquare, so that needs to be updated too */
\r
2516 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2518 HBITMAP oldBitmap;
\r
2522 if (appData.blindfold) return;
\r
2524 /* [AS] Use font-based pieces if needed */
\r
2525 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2526 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2527 CreatePiecesFromFont();
\r
2529 if( fontBitmapSquareSize == squareSize ) {
\r
2530 int index = TranslatePieceToFontPiece(piece);
\r
2532 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2534 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2535 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2539 squareSize, squareSize,
\r
2544 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2546 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2547 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2551 squareSize, squareSize,
\r
2560 if (appData.monoMode) {
\r
2561 SelectObject(tmphdc, PieceBitmap(piece,
\r
2562 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2563 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2564 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2566 tmpSize = squareSize;
\r
2568 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2569 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2570 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2571 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2572 x += (squareSize - minorSize)>>1;
\r
2573 y += squareSize - minorSize - 2;
\r
2574 tmpSize = minorSize;
\r
2576 if (color || appData.allWhite ) {
\r
2577 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2579 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2580 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2581 if(appData.upsideDown && color==flipView)
\r
2582 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2584 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2585 /* Use black for outline of white pieces */
\r
2586 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2587 if(appData.upsideDown && color==flipView)
\r
2588 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2590 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2592 /* Use square color for details of black pieces */
\r
2593 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2594 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2595 if(appData.upsideDown && !flipView)
\r
2596 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2598 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2600 SelectObject(hdc, oldBrush);
\r
2601 SelectObject(tmphdc, oldBitmap);
\r
2605 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2606 int GetBackTextureMode( int algo )
\r
2608 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2612 case BACK_TEXTURE_MODE_PLAIN:
\r
2613 result = 1; /* Always use identity map */
\r
2615 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2616 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2624 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2625 to handle redraws cleanly (as random numbers would always be different).
\r
2627 VOID RebuildTextureSquareInfo()
\r
2637 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2639 if( liteBackTexture != NULL ) {
\r
2640 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2641 lite_w = bi.bmWidth;
\r
2642 lite_h = bi.bmHeight;
\r
2646 if( darkBackTexture != NULL ) {
\r
2647 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2648 dark_w = bi.bmWidth;
\r
2649 dark_h = bi.bmHeight;
\r
2653 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2654 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2655 if( (col + row) & 1 ) {
\r
2657 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2658 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2659 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2661 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2662 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2663 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2665 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2666 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2671 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2672 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2673 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2675 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2676 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2677 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2679 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2680 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2687 /* [AS] Arrow highlighting support */
\r
2689 static int A_WIDTH = 5; /* Width of arrow body */
\r
2691 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2692 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2694 static double Sqr( double x )
\r
2699 static int Round( double x )
\r
2701 return (int) (x + 0.5);
\r
2704 /* Draw an arrow between two points using current settings */
\r
2705 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2708 double dx, dy, j, k, x, y;
\r
2710 if( d_x == s_x ) {
\r
2711 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2713 arrow[0].x = s_x + A_WIDTH;
\r
2716 arrow[1].x = s_x + A_WIDTH;
\r
2717 arrow[1].y = d_y - h;
\r
2719 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2720 arrow[2].y = d_y - h;
\r
2725 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2726 arrow[4].y = d_y - h;
\r
2728 arrow[5].x = s_x - A_WIDTH;
\r
2729 arrow[5].y = d_y - h;
\r
2731 arrow[6].x = s_x - A_WIDTH;
\r
2734 else if( d_y == s_y ) {
\r
2735 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2738 arrow[0].y = s_y + A_WIDTH;
\r
2740 arrow[1].x = d_x - w;
\r
2741 arrow[1].y = s_y + A_WIDTH;
\r
2743 arrow[2].x = d_x - w;
\r
2744 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2749 arrow[4].x = d_x - w;
\r
2750 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2752 arrow[5].x = d_x - w;
\r
2753 arrow[5].y = s_y - A_WIDTH;
\r
2756 arrow[6].y = s_y - A_WIDTH;
\r
2759 /* [AS] Needed a lot of paper for this! :-) */
\r
2760 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2761 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2763 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2765 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2770 arrow[0].x = Round(x - j);
\r
2771 arrow[0].y = Round(y + j*dx);
\r
2773 arrow[1].x = Round(x + j);
\r
2774 arrow[1].y = Round(y - j*dx);
\r
2777 x = (double) d_x - k;
\r
2778 y = (double) d_y - k*dy;
\r
2781 x = (double) d_x + k;
\r
2782 y = (double) d_y + k*dy;
\r
2785 arrow[2].x = Round(x + j);
\r
2786 arrow[2].y = Round(y - j*dx);
\r
2788 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2789 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2794 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2795 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2797 arrow[6].x = Round(x - j);
\r
2798 arrow[6].y = Round(y + j*dx);
\r
2801 Polygon( hdc, arrow, 7 );
\r
2804 /* [AS] Draw an arrow between two squares */
\r
2805 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2807 int s_x, s_y, d_x, d_y;
\r
2814 if( s_col == d_col && s_row == d_row ) {
\r
2818 /* Get source and destination points */
\r
2819 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2820 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2823 d_y += squareSize / 4;
\r
2825 else if( d_y < s_y ) {
\r
2826 d_y += 3 * squareSize / 4;
\r
2829 d_y += squareSize / 2;
\r
2833 d_x += squareSize / 4;
\r
2835 else if( d_x < s_x ) {
\r
2836 d_x += 3 * squareSize / 4;
\r
2839 d_x += squareSize / 2;
\r
2842 s_x += squareSize / 2;
\r
2843 s_y += squareSize / 2;
\r
2845 /* Adjust width */
\r
2846 A_WIDTH = squareSize / 14;
\r
2849 stLB.lbStyle = BS_SOLID;
\r
2850 stLB.lbColor = appData.highlightArrowColor;
\r
2853 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2854 holdpen = SelectObject( hdc, hpen );
\r
2855 hbrush = CreateBrushIndirect( &stLB );
\r
2856 holdbrush = SelectObject( hdc, hbrush );
\r
2858 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2860 SelectObject( hdc, holdpen );
\r
2861 SelectObject( hdc, holdbrush );
\r
2862 DeleteObject( hpen );
\r
2863 DeleteObject( hbrush );
\r
2866 BOOL HasHighlightInfo()
\r
2868 BOOL result = FALSE;
\r
2870 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2871 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2879 BOOL IsDrawArrowEnabled()
\r
2881 BOOL result = FALSE;
\r
2883 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2890 VOID DrawArrowHighlight( HDC hdc )
\r
2892 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2893 DrawArrowBetweenSquares( hdc,
\r
2894 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2895 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2899 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2901 HRGN result = NULL;
\r
2903 if( HasHighlightInfo() ) {
\r
2904 int x1, y1, x2, y2;
\r
2905 int sx, sy, dx, dy;
\r
2907 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2908 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2910 sx = MIN( x1, x2 );
\r
2911 sy = MIN( y1, y2 );
\r
2912 dx = MAX( x1, x2 ) + squareSize;
\r
2913 dy = MAX( y1, y2 ) + squareSize;
\r
2915 result = CreateRectRgn( sx, sy, dx, dy );
\r
2922 Warning: this function modifies the behavior of several other functions.
\r
2924 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2925 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2926 repaint is scattered all over the place, which is not good for features such as
\r
2927 "arrow highlighting" that require a full repaint of the board.
\r
2929 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2930 user interaction, when speed is not so important) but especially to avoid errors
\r
2931 in the displayed graphics.
\r
2933 In such patched places, I always try refer to this function so there is a single
\r
2934 place to maintain knowledge.
\r
2936 To restore the original behavior, just return FALSE unconditionally.
\r
2938 BOOL IsFullRepaintPreferrable()
\r
2940 BOOL result = FALSE;
\r
2942 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2943 /* Arrow may appear on the board */
\r
2951 This function is called by DrawPosition to know whether a full repaint must
\r
2954 Only DrawPosition may directly call this function, which makes use of
\r
2955 some state information. Other function should call DrawPosition specifying
\r
2956 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2958 BOOL DrawPositionNeedsFullRepaint()
\r
2960 BOOL result = FALSE;
\r
2963 Probably a slightly better policy would be to trigger a full repaint
\r
2964 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2965 but animation is fast enough that it's difficult to notice.
\r
2967 if( animInfo.piece == EmptySquare ) {
\r
2968 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2977 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2979 int row, column, x, y, square_color, piece_color;
\r
2980 ChessSquare piece;
\r
2982 HDC texture_hdc = NULL;
\r
2984 /* [AS] Initialize background textures if needed */
\r
2985 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2986 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2987 if( backTextureSquareSize != squareSize
\r
2988 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2989 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2990 backTextureSquareSize = squareSize;
\r
2991 RebuildTextureSquareInfo();
\r
2994 texture_hdc = CreateCompatibleDC( hdc );
\r
2997 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2998 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3000 SquareToPos(row, column, &x, &y);
\r
3002 piece = board[row][column];
\r
3004 square_color = ((column + row) % 2) == 1;
\r
3005 if( gameInfo.variant == VariantXiangqi ) {
\r
3006 square_color = !InPalace(row, column);
\r
3007 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3008 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3010 piece_color = (int) piece < (int) BlackPawn;
\r
3013 /* [HGM] holdings file: light square or black */
\r
3014 if(column == BOARD_LEFT-2) {
\r
3015 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3018 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3022 if(column == BOARD_RGHT + 1 ) {
\r
3023 if( row < gameInfo.holdingsSize )
\r
3026 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3030 if(column == BOARD_LEFT-1 ) /* left align */
\r
3031 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3032 else if( column == BOARD_RGHT) /* right align */
\r
3033 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3035 if (appData.monoMode) {
\r
3036 if (piece == EmptySquare) {
\r
3037 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3038 square_color ? WHITENESS : BLACKNESS);
\r
3040 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3043 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3044 /* [AS] Draw the square using a texture bitmap */
\r
3045 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3046 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3047 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3050 squareSize, squareSize,
\r
3053 backTextureSquareInfo[r][c].mode,
\r
3054 backTextureSquareInfo[r][c].x,
\r
3055 backTextureSquareInfo[r][c].y );
\r
3057 SelectObject( texture_hdc, hbm );
\r
3059 if (piece != EmptySquare) {
\r
3060 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3064 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3066 oldBrush = SelectObject(hdc, brush );
\r
3067 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3068 SelectObject(hdc, oldBrush);
\r
3069 if (piece != EmptySquare)
\r
3070 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3075 if( texture_hdc != NULL ) {
\r
3076 DeleteDC( texture_hdc );
\r
3080 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3081 void fputDW(FILE *f, int x)
\r
3083 fputc(x & 255, f);
\r
3084 fputc(x>>8 & 255, f);
\r
3085 fputc(x>>16 & 255, f);
\r
3086 fputc(x>>24 & 255, f);
\r
3089 #define MAX_CLIPS 200 /* more than enough */
\r
3092 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3094 // HBITMAP bufferBitmap;
\r
3099 int w = 100, h = 50;
\r
3101 if(logo == NULL) return;
\r
3102 // GetClientRect(hwndMain, &Rect);
\r
3103 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3104 // Rect.bottom-Rect.top+1);
\r
3105 tmphdc = CreateCompatibleDC(hdc);
\r
3106 hbm = SelectObject(tmphdc, logo);
\r
3107 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3111 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3112 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3113 SelectObject(tmphdc, hbm);
\r
3117 static HDC hdcSeek;
\r
3119 // [HGM] seekgraph
\r
3120 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3123 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3124 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3125 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3126 SelectObject( hdcSeek, hp );
\r
3129 // front-end wrapper for drawing functions to do rectangles
\r
3130 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3135 if (hdcSeek == NULL) {
\r
3136 hdcSeek = GetDC(hwndMain);
\r
3137 if (!appData.monoMode) {
\r
3138 SelectPalette(hdcSeek, hPal, FALSE);
\r
3139 RealizePalette(hdcSeek);
\r
3142 hp = SelectObject( hdcSeek, gridPen );
\r
3143 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3144 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3145 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3146 SelectObject( hdcSeek, hp );
\r
3149 // front-end wrapper for putting text in graph
\r
3150 void DrawSeekText(char *buf, int x, int y)
\r
3153 SetBkMode( hdcSeek, TRANSPARENT );
\r
3154 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3155 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3158 void DrawSeekDot(int x, int y, int color)
\r
3160 int square = color & 0x80;
\r
3162 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3163 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3165 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3166 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3168 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3169 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3170 SelectObject(hdcSeek, oldBrush);
\r
3174 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3176 static Board lastReq[2], lastDrawn[2];
\r
3177 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3178 static int lastDrawnFlipView = 0;
\r
3179 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3180 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3183 HBITMAP bufferBitmap;
\r
3184 HBITMAP oldBitmap;
\r
3186 HRGN clips[MAX_CLIPS];
\r
3187 ChessSquare dragged_piece = EmptySquare;
\r
3188 int nr = twoBoards*partnerUp;
\r
3190 /* I'm undecided on this - this function figures out whether a full
\r
3191 * repaint is necessary on its own, so there's no real reason to have the
\r
3192 * caller tell it that. I think this can safely be set to FALSE - but
\r
3193 * if we trust the callers not to request full repaints unnessesarily, then
\r
3194 * we could skip some clipping work. In other words, only request a full
\r
3195 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3196 * gamestart and similar) --Hawk
\r
3198 Boolean fullrepaint = repaint;
\r
3200 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3202 if( DrawPositionNeedsFullRepaint() ) {
\r
3203 fullrepaint = TRUE;
\r
3206 if (board == NULL) {
\r
3207 if (!lastReqValid[nr]) {
\r
3210 board = lastReq[nr];
\r
3212 CopyBoard(lastReq[nr], board);
\r
3213 lastReqValid[nr] = 1;
\r
3216 if (doingSizing) {
\r
3220 if (IsIconic(hwndMain)) {
\r
3224 if (hdc == NULL) {
\r
3225 hdc = GetDC(hwndMain);
\r
3226 if (!appData.monoMode) {
\r
3227 SelectPalette(hdc, hPal, FALSE);
\r
3228 RealizePalette(hdc);
\r
3232 releaseDC = FALSE;
\r
3235 /* Create some work-DCs */
\r
3236 hdcmem = CreateCompatibleDC(hdc);
\r
3237 tmphdc = CreateCompatibleDC(hdc);
\r
3239 /* If dragging is in progress, we temporarely remove the piece */
\r
3240 /* [HGM] or temporarily decrease count if stacked */
\r
3241 /* !! Moved to before board compare !! */
\r
3242 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3243 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3244 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3245 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3246 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3248 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3249 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3250 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3252 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3255 /* Figure out which squares need updating by comparing the
\r
3256 * newest board with the last drawn board and checking if
\r
3257 * flipping has changed.
\r
3259 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3260 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3261 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3262 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3263 SquareToPos(row, column, &x, &y);
\r
3264 clips[num_clips++] =
\r
3265 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3269 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3270 for (i=0; i<2; i++) {
\r
3271 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3272 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3273 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3274 lastDrawnHighlight.sq[i].y >= 0) {
\r
3275 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3276 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3277 clips[num_clips++] =
\r
3278 CreateRectRgn(x - lineGap, y - lineGap,
\r
3279 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3281 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3282 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3283 clips[num_clips++] =
\r
3284 CreateRectRgn(x - lineGap, y - lineGap,
\r
3285 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3289 for (i=0; i<2; i++) {
\r
3290 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3291 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3292 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3293 lastDrawnPremove.sq[i].y >= 0) {
\r
3294 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3295 lastDrawnPremove.sq[i].x, &x, &y);
\r
3296 clips[num_clips++] =
\r
3297 CreateRectRgn(x - lineGap, y - lineGap,
\r
3298 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3300 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3301 premoveHighlightInfo.sq[i].y >= 0) {
\r
3302 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3303 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3304 clips[num_clips++] =
\r
3305 CreateRectRgn(x - lineGap, y - lineGap,
\r
3306 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3310 } else { // nr == 1
\r
3311 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3312 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3313 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3314 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3315 for (i=0; i<2; i++) {
\r
3316 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3317 partnerHighlightInfo.sq[i].y >= 0) {
\r
3318 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3319 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3320 clips[num_clips++] =
\r
3321 CreateRectRgn(x - lineGap, y - lineGap,
\r
3322 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3324 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3325 oldPartnerHighlight.sq[i].y >= 0) {
\r
3326 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3327 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3328 clips[num_clips++] =
\r
3329 CreateRectRgn(x - lineGap, y - lineGap,
\r
3330 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3335 fullrepaint = TRUE;
\r
3338 /* Create a buffer bitmap - this is the actual bitmap
\r
3339 * being written to. When all the work is done, we can
\r
3340 * copy it to the real DC (the screen). This avoids
\r
3341 * the problems with flickering.
\r
3343 GetClientRect(hwndMain, &Rect);
\r
3344 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3345 Rect.bottom-Rect.top+1);
\r
3346 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3347 if (!appData.monoMode) {
\r
3348 SelectPalette(hdcmem, hPal, FALSE);
\r
3351 /* Create clips for dragging */
\r
3352 if (!fullrepaint) {
\r
3353 if (dragInfo.from.x >= 0) {
\r
3354 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3355 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3357 if (dragInfo.start.x >= 0) {
\r
3358 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3359 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3361 if (dragInfo.pos.x >= 0) {
\r
3362 x = dragInfo.pos.x - squareSize / 2;
\r
3363 y = dragInfo.pos.y - squareSize / 2;
\r
3364 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3366 if (dragInfo.lastpos.x >= 0) {
\r
3367 x = dragInfo.lastpos.x - squareSize / 2;
\r
3368 y = dragInfo.lastpos.y - squareSize / 2;
\r
3369 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3373 /* Are we animating a move?
\r
3375 * - remove the piece from the board (temporarely)
\r
3376 * - calculate the clipping region
\r
3378 if (!fullrepaint) {
\r
3379 if (animInfo.piece != EmptySquare) {
\r
3380 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3381 x = boardRect.left + animInfo.lastpos.x;
\r
3382 y = boardRect.top + animInfo.lastpos.y;
\r
3383 x2 = boardRect.left + animInfo.pos.x;
\r
3384 y2 = boardRect.top + animInfo.pos.y;
\r
3385 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3386 /* Slight kludge. The real problem is that after AnimateMove is
\r
3387 done, the position on the screen does not match lastDrawn.
\r
3388 This currently causes trouble only on e.p. captures in
\r
3389 atomic, where the piece moves to an empty square and then
\r
3390 explodes. The old and new positions both had an empty square
\r
3391 at the destination, but animation has drawn a piece there and
\r
3392 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3393 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3397 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3398 if (num_clips == 0)
\r
3399 fullrepaint = TRUE;
\r
3401 /* Set clipping on the memory DC */
\r
3402 if (!fullrepaint) {
\r
3403 SelectClipRgn(hdcmem, clips[0]);
\r
3404 for (x = 1; x < num_clips; x++) {
\r
3405 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3406 abort(); // this should never ever happen!
\r
3410 /* Do all the drawing to the memory DC */
\r
3411 if(explodeInfo.radius) { // [HGM] atomic
\r
3413 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3414 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3415 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3416 x += squareSize/2;
\r
3417 y += squareSize/2;
\r
3418 if(!fullrepaint) {
\r
3419 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3420 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3422 DrawGridOnDC(hdcmem);
\r
3423 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3424 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3425 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3426 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3427 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3428 SelectObject(hdcmem, oldBrush);
\r
3430 DrawGridOnDC(hdcmem);
\r
3431 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3432 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3433 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3435 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3436 oldPartnerHighlight = partnerHighlightInfo;
\r
3438 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3440 if(nr == 0) // [HGM] dual: markers only on left board
\r
3441 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3442 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3443 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3444 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3445 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3446 SquareToPos(row, column, &x, &y);
\r
3447 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3448 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3449 SelectObject(hdcmem, oldBrush);
\r
3454 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3455 if(appData.autoLogo) {
\r
3457 switch(gameMode) { // pick logos based on game mode
\r
3458 case IcsObserving:
\r
3459 whiteLogo = second.programLogo; // ICS logo
\r
3460 blackLogo = second.programLogo;
\r
3463 case IcsPlayingWhite:
\r
3464 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3465 blackLogo = second.programLogo; // ICS logo
\r
3467 case IcsPlayingBlack:
\r
3468 whiteLogo = second.programLogo; // ICS logo
\r
3469 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3471 case TwoMachinesPlay:
\r
3472 if(first.twoMachinesColor[0] == 'b') {
\r
3473 whiteLogo = second.programLogo;
\r
3474 blackLogo = first.programLogo;
\r
3477 case MachinePlaysWhite:
\r
3478 blackLogo = userLogo;
\r
3480 case MachinePlaysBlack:
\r
3481 whiteLogo = userLogo;
\r
3482 blackLogo = first.programLogo;
\r
3485 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3486 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3489 if( appData.highlightMoveWithArrow ) {
\r
3490 DrawArrowHighlight(hdcmem);
\r
3493 DrawCoordsOnDC(hdcmem);
\r
3495 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3496 /* to make sure lastDrawn contains what is actually drawn */
\r
3498 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3499 if (dragged_piece != EmptySquare) {
\r
3500 /* [HGM] or restack */
\r
3501 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3502 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3504 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3505 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3506 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3507 x = dragInfo.pos.x - squareSize / 2;
\r
3508 y = dragInfo.pos.y - squareSize / 2;
\r
3509 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3510 ((int) dragged_piece < (int) BlackPawn),
\r
3511 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3514 /* Put the animated piece back into place and draw it */
\r
3515 if (animInfo.piece != EmptySquare) {
\r
3516 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3517 x = boardRect.left + animInfo.pos.x;
\r
3518 y = boardRect.top + animInfo.pos.y;
\r
3519 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3520 ((int) animInfo.piece < (int) BlackPawn),
\r
3521 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3524 /* Release the bufferBitmap by selecting in the old bitmap
\r
3525 * and delete the memory DC
\r
3527 SelectObject(hdcmem, oldBitmap);
\r
3530 /* Set clipping on the target DC */
\r
3531 if (!fullrepaint) {
\r
3532 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3534 GetRgnBox(clips[x], &rect);
\r
3535 DeleteObject(clips[x]);
\r
3536 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3537 rect.right + wpMain.width/2, rect.bottom);
\r
3539 SelectClipRgn(hdc, clips[0]);
\r
3540 for (x = 1; x < num_clips; x++) {
\r
3541 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3542 abort(); // this should never ever happen!
\r
3546 /* Copy the new bitmap onto the screen in one go.
\r
3547 * This way we avoid any flickering
\r
3549 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3550 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3551 boardRect.right - boardRect.left,
\r
3552 boardRect.bottom - boardRect.top,
\r
3553 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3554 if(saveDiagFlag) {
\r
3555 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3556 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3558 GetObject(bufferBitmap, sizeof(b), &b);
\r
3559 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3560 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3561 bih.biWidth = b.bmWidth;
\r
3562 bih.biHeight = b.bmHeight;
\r
3564 bih.biBitCount = b.bmBitsPixel;
\r
3565 bih.biCompression = 0;
\r
3566 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3567 bih.biXPelsPerMeter = 0;
\r
3568 bih.biYPelsPerMeter = 0;
\r
3569 bih.biClrUsed = 0;
\r
3570 bih.biClrImportant = 0;
\r
3571 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3572 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3573 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3574 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3576 wb = b.bmWidthBytes;
\r
3578 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3579 int k = ((int*) pData)[i];
\r
3580 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3581 if(j >= 16) break;
\r
3583 if(j >= nrColors) nrColors = j+1;
\r
3585 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3587 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3588 for(w=0; w<(wb>>2); w+=2) {
\r
3589 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3590 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3591 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3592 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3593 pData[p++] = m | j<<4;
\r
3595 while(p&3) pData[p++] = 0;
\r
3598 wb = ((wb+31)>>5)<<2;
\r
3600 // write BITMAPFILEHEADER
\r
3601 fprintf(diagFile, "BM");
\r
3602 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3603 fputDW(diagFile, 0);
\r
3604 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3605 // write BITMAPINFOHEADER
\r
3606 fputDW(diagFile, 40);
\r
3607 fputDW(diagFile, b.bmWidth);
\r
3608 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3609 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3610 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3611 fputDW(diagFile, 0);
\r
3612 fputDW(diagFile, 0);
\r
3613 fputDW(diagFile, 0);
\r
3614 fputDW(diagFile, 0);
\r
3615 fputDW(diagFile, 0);
\r
3616 fputDW(diagFile, 0);
\r
3617 // write color table
\r
3619 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3620 // write bitmap data
\r
3621 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3622 fputc(pData[i], diagFile);
\r
3626 SelectObject(tmphdc, oldBitmap);
\r
3628 /* Massive cleanup */
\r
3629 for (x = 0; x < num_clips; x++)
\r
3630 DeleteObject(clips[x]);
\r
3633 DeleteObject(bufferBitmap);
\r
3636 ReleaseDC(hwndMain, hdc);
\r
3638 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3640 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3642 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3645 /* CopyBoard(lastDrawn, board);*/
\r
3646 lastDrawnHighlight = highlightInfo;
\r
3647 lastDrawnPremove = premoveHighlightInfo;
\r
3648 lastDrawnFlipView = flipView;
\r
3649 lastDrawnValid[nr] = 1;
\r
3652 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3657 saveDiagFlag = 1; diagFile = f;
\r
3658 HDCDrawPosition(NULL, TRUE, NULL);
\r
3662 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3669 /*---------------------------------------------------------------------------*\
\r
3670 | CLIENT PAINT PROCEDURE
\r
3671 | This is the main event-handler for the WM_PAINT message.
\r
3673 \*---------------------------------------------------------------------------*/
\r
3675 PaintProc(HWND hwnd)
\r
3681 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3682 if (IsIconic(hwnd)) {
\r
3683 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3685 if (!appData.monoMode) {
\r
3686 SelectPalette(hdc, hPal, FALSE);
\r
3687 RealizePalette(hdc);
\r
3689 HDCDrawPosition(hdc, 1, NULL);
\r
3690 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3691 flipView = !flipView; partnerUp = !partnerUp;
\r
3692 HDCDrawPosition(hdc, 1, NULL);
\r
3693 flipView = !flipView; partnerUp = !partnerUp;
\r
3696 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3697 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3698 ETO_CLIPPED|ETO_OPAQUE,
\r
3699 &messageRect, messageText, strlen(messageText), NULL);
\r
3700 SelectObject(hdc, oldFont);
\r
3701 DisplayBothClocks();
\r
3703 EndPaint(hwnd,&ps);
\r
3711 * If the user selects on a border boundary, return -1; if off the board,
\r
3712 * return -2. Otherwise map the event coordinate to the square.
\r
3713 * The offset boardRect.left or boardRect.top must already have been
\r
3714 * subtracted from x.
\r
3716 int EventToSquare(x, limit)
\r
3724 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3726 x /= (squareSize + lineGap);
\r
3738 DropEnable dropEnables[] = {
\r
3739 { 'P', DP_Pawn, "Pawn" },
\r
3740 { 'N', DP_Knight, "Knight" },
\r
3741 { 'B', DP_Bishop, "Bishop" },
\r
3742 { 'R', DP_Rook, "Rook" },
\r
3743 { 'Q', DP_Queen, "Queen" },
\r
3747 SetupDropMenu(HMENU hmenu)
\r
3749 int i, count, enable;
\r
3751 extern char white_holding[], black_holding[];
\r
3752 char item[MSG_SIZ];
\r
3754 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3755 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3756 dropEnables[i].piece);
\r
3758 while (p && *p++ == dropEnables[i].piece) count++;
\r
3759 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3760 enable = count > 0 || !appData.testLegality
\r
3761 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3762 && !appData.icsActive);
\r
3763 ModifyMenu(hmenu, dropEnables[i].command,
\r
3764 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3765 dropEnables[i].command, item);
\r
3769 void DragPieceBegin(int x, int y)
\r
3771 dragInfo.lastpos.x = boardRect.left + x;
\r
3772 dragInfo.lastpos.y = boardRect.top + y;
\r
3773 dragInfo.from.x = fromX;
\r
3774 dragInfo.from.y = fromY;
\r
3775 dragInfo.start = dragInfo.from;
\r
3776 SetCapture(hwndMain);
\r
3779 void DragPieceEnd(int x, int y)
\r
3782 dragInfo.start.x = dragInfo.start.y = -1;
\r
3783 dragInfo.from = dragInfo.start;
\r
3784 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3787 /* Event handler for mouse messages */
\r
3789 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3793 static int recursive = 0;
\r
3795 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3798 if (message == WM_MBUTTONUP) {
\r
3799 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3800 to the middle button: we simulate pressing the left button too!
\r
3802 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3803 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3809 pt.x = LOWORD(lParam);
\r
3810 pt.y = HIWORD(lParam);
\r
3811 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3812 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3813 if (!flipView && y >= 0) {
\r
3814 y = BOARD_HEIGHT - 1 - y;
\r
3816 if (flipView && x >= 0) {
\r
3817 x = BOARD_WIDTH - 1 - x;
\r
3820 switch (message) {
\r
3821 case WM_LBUTTONDOWN:
\r
3822 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3823 if (gameMode == EditPosition) {
\r
3824 SetWhiteToPlayEvent();
\r
3825 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3826 AdjustClock(flipClock, -1);
\r
3827 } else if (gameMode == IcsPlayingBlack ||
\r
3828 gameMode == MachinePlaysWhite) {
\r
3831 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3832 if (gameMode == EditPosition) {
\r
3833 SetBlackToPlayEvent();
\r
3834 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3835 AdjustClock(!flipClock, -1);
\r
3836 } else if (gameMode == IcsPlayingWhite ||
\r
3837 gameMode == MachinePlaysBlack) {
\r
3841 dragInfo.start.x = dragInfo.start.y = -1;
\r
3842 dragInfo.from = dragInfo.start;
\r
3843 if(fromX == -1 && frozen) { // not sure where this is for
\r
3844 fromX = fromY = -1;
\r
3845 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3848 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3849 DrawPosition(TRUE, NULL);
\r
3852 case WM_LBUTTONUP:
\r
3853 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3854 DrawPosition(TRUE, NULL);
\r
3857 case WM_MOUSEMOVE:
\r
3858 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3859 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3860 if ((appData.animateDragging || appData.highlightDragging)
\r
3861 && (wParam & MK_LBUTTON)
\r
3862 && dragInfo.from.x >= 0)
\r
3864 BOOL full_repaint = FALSE;
\r
3866 if (appData.animateDragging) {
\r
3867 dragInfo.pos = pt;
\r
3869 if (appData.highlightDragging) {
\r
3870 SetHighlights(fromX, fromY, x, y);
\r
3871 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3872 full_repaint = TRUE;
\r
3876 DrawPosition( full_repaint, NULL);
\r
3878 dragInfo.lastpos = dragInfo.pos;
\r
3882 case WM_MOUSEWHEEL: // [DM]
\r
3883 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3884 /* Mouse Wheel is being rolled forward
\r
3885 * Play moves forward
\r
3887 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
3888 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3889 /* Mouse Wheel is being rolled backward
\r
3890 * Play moves backward
\r
3892 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
3893 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3897 case WM_MBUTTONUP:
\r
3898 case WM_RBUTTONUP:
\r
3900 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3903 case WM_MBUTTONDOWN:
\r
3904 case WM_RBUTTONDOWN:
\r
3907 fromX = fromY = -1;
\r
3908 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3909 dragInfo.start.x = dragInfo.start.y = -1;
\r
3910 dragInfo.from = dragInfo.start;
\r
3911 dragInfo.lastpos = dragInfo.pos;
\r
3912 if (appData.highlightDragging) {
\r
3913 ClearHighlights();
\r
3916 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3917 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3918 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
3919 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3920 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
3924 DrawPosition(TRUE, NULL);
\r
3926 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3929 if (message == WM_MBUTTONDOWN) {
\r
3930 buttonCount = 3; /* even if system didn't think so */
\r
3931 if (wParam & MK_SHIFT)
\r
3932 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3934 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3935 } else { /* message == WM_RBUTTONDOWN */
\r
3936 /* Just have one menu, on the right button. Windows users don't
\r
3937 think to try the middle one, and sometimes other software steals
\r
3938 it, or it doesn't really exist. */
\r
3939 if(gameInfo.variant != VariantShogi)
\r
3940 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3942 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3946 SetCapture(hwndMain);
3949 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3950 SetupDropMenu(hmenu);
\r
3951 MenuPopup(hwnd, pt, hmenu, -1);
\r
3961 /* Preprocess messages for buttons in main window */
\r
3963 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3965 int id = GetWindowLong(hwnd, GWL_ID);
\r
3968 for (i=0; i<N_BUTTONS; i++) {
\r
3969 if (buttonDesc[i].id == id) break;
\r
3971 if (i == N_BUTTONS) return 0;
\r
3972 switch (message) {
\r
3977 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3978 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3985 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3988 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3989 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3990 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3991 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3993 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3995 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3996 PopUpMoveDialog((char)wParam);
\r
4002 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4005 /* Process messages for Promotion dialog box */
\r
4007 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4011 switch (message) {
\r
4012 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4013 /* Center the dialog over the application window */
\r
4014 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4015 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4016 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4017 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4018 SW_SHOW : SW_HIDE);
\r
4019 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4020 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4021 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4022 PieceToChar(WhiteAngel) != '~') ||
\r
4023 (PieceToChar(BlackAngel) >= 'A' &&
\r
4024 PieceToChar(BlackAngel) != '~') ) ?
\r
4025 SW_SHOW : SW_HIDE);
\r
4026 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4027 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4028 PieceToChar(WhiteMarshall) != '~') ||
\r
4029 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4030 PieceToChar(BlackMarshall) != '~') ) ?
\r
4031 SW_SHOW : SW_HIDE);
\r
4032 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4033 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4034 gameInfo.variant != VariantShogi ?
\r
4035 SW_SHOW : SW_HIDE);
\r
4036 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4037 gameInfo.variant != VariantShogi ?
\r
4038 SW_SHOW : SW_HIDE);
\r
4039 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4040 gameInfo.variant == VariantShogi ?
\r
4041 SW_SHOW : SW_HIDE);
\r
4042 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4043 gameInfo.variant == VariantShogi ?
\r
4044 SW_SHOW : SW_HIDE);
\r
4045 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4046 gameInfo.variant == VariantSuper ?
\r
4047 SW_SHOW : SW_HIDE);
\r
4050 case WM_COMMAND: /* message: received a command */
\r
4051 switch (LOWORD(wParam)) {
\r
4053 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4054 ClearHighlights();
\r
4055 DrawPosition(FALSE, NULL);
\r
4058 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4061 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4064 promoChar = PieceToChar(BlackRook);
\r
4067 promoChar = PieceToChar(BlackBishop);
\r
4069 case PB_Chancellor:
\r
4070 promoChar = PieceToChar(BlackMarshall);
\r
4072 case PB_Archbishop:
\r
4073 promoChar = PieceToChar(BlackAngel);
\r
4076 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4081 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4082 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4083 only show the popup when we are already sure the move is valid or
\r
4084 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4085 will figure out it is a promotion from the promoChar. */
\r
4086 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4087 fromX = fromY = -1;
\r
4088 if (!appData.highlightLastMove) {
\r
4089 ClearHighlights();
\r
4090 DrawPosition(FALSE, NULL);
\r
4097 /* Pop up promotion dialog */
\r
4099 PromotionPopup(HWND hwnd)
\r
4103 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4104 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4105 hwnd, (DLGPROC)lpProc);
\r
4106 FreeProcInstance(lpProc);
\r
4112 DrawPosition(TRUE, NULL);
\r
4113 PromotionPopup(hwndMain);
\r
4116 /* Toggle ShowThinking */
\r
4118 ToggleShowThinking()
\r
4120 appData.showThinking = !appData.showThinking;
\r
4121 ShowThinkingEvent();
\r
4125 LoadGameDialog(HWND hwnd, char* title)
\r
4129 char fileTitle[MSG_SIZ];
\r
4130 f = OpenFileDialog(hwnd, "rb", "",
\r
4131 appData.oldSaveStyle ? "gam" : "pgn",
\r
4133 title, &number, fileTitle, NULL);
\r
4135 cmailMsgLoaded = FALSE;
\r
4136 if (number == 0) {
\r
4137 int error = GameListBuild(f);
\r
4139 DisplayError("Cannot build game list", error);
\r
4140 } else if (!ListEmpty(&gameList) &&
\r
4141 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4142 GameListPopUp(f, fileTitle);
\r
4145 GameListDestroy();
\r
4148 LoadGame(f, number, fileTitle, FALSE);
\r
4152 int get_term_width()
\r
4157 HFONT hfont, hold_font;
\r
4162 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4166 // get the text metrics
\r
4167 hdc = GetDC(hText);
\r
4168 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4169 if (consoleCF.dwEffects & CFE_BOLD)
\r
4170 lf.lfWeight = FW_BOLD;
\r
4171 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4172 lf.lfItalic = TRUE;
\r
4173 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4174 lf.lfStrikeOut = TRUE;
\r
4175 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4176 lf.lfUnderline = TRUE;
\r
4177 hfont = CreateFontIndirect(&lf);
\r
4178 hold_font = SelectObject(hdc, hfont);
\r
4179 GetTextMetrics(hdc, &tm);
\r
4180 SelectObject(hdc, hold_font);
\r
4181 DeleteObject(hfont);
\r
4182 ReleaseDC(hText, hdc);
\r
4184 // get the rectangle
\r
4185 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4187 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4190 void UpdateICSWidth(HWND hText)
\r
4192 LONG old_width, new_width;
\r
4194 new_width = get_term_width(hText, FALSE);
\r
4195 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4196 if (new_width != old_width)
\r
4198 ics_update_width(new_width);
\r
4199 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4204 ChangedConsoleFont()
\r
4207 CHARRANGE tmpsel, sel;
\r
4208 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4209 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4210 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4213 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4214 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4215 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4216 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4217 * size. This was undocumented in the version of MSVC++ that I had
\r
4218 * when I wrote the code, but is apparently documented now.
\r
4220 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4221 cfmt.bCharSet = f->lf.lfCharSet;
\r
4222 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4223 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4224 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4225 /* Why are the following seemingly needed too? */
\r
4226 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4227 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4228 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4230 tmpsel.cpMax = -1; /*999999?*/
\r
4231 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4232 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4233 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4234 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4236 paraf.cbSize = sizeof(paraf);
\r
4237 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4238 paraf.dxStartIndent = 0;
\r
4239 paraf.dxOffset = WRAP_INDENT;
\r
4240 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4241 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4242 UpdateICSWidth(hText);
\r
4245 /*---------------------------------------------------------------------------*\
\r
4247 * Window Proc for main window
\r
4249 \*---------------------------------------------------------------------------*/
\r
4251 /* Process messages for main window, etc. */
\r
4253 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4256 int wmId, wmEvent;
\r
4260 char fileTitle[MSG_SIZ];
\r
4261 char buf[MSG_SIZ];
\r
4262 static SnapData sd;
\r
4264 switch (message) {
\r
4266 case WM_PAINT: /* message: repaint portion of window */
\r
4270 case WM_ERASEBKGND:
\r
4271 if (IsIconic(hwnd)) {
\r
4272 /* Cheat; change the message */
\r
4273 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4275 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4279 case WM_LBUTTONDOWN:
\r
4280 case WM_MBUTTONDOWN:
\r
4281 case WM_RBUTTONDOWN:
\r
4282 case WM_LBUTTONUP:
\r
4283 case WM_MBUTTONUP:
\r
4284 case WM_RBUTTONUP:
\r
4285 case WM_MOUSEMOVE:
\r
4286 case WM_MOUSEWHEEL:
\r
4287 MouseEvent(hwnd, message, wParam, lParam);
\r
4290 JAWS_KB_NAVIGATION
\r
4294 JAWS_ALT_INTERCEPT
\r
4296 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4297 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4298 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4299 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4301 SendMessage(h, message, wParam, lParam);
\r
4302 } else if(lParam != KF_REPEAT) {
\r
4303 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4304 PopUpMoveDialog((char)wParam);
\r
4305 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4306 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4311 case WM_PALETTECHANGED:
\r
4312 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4314 HDC hdc = GetDC(hwndMain);
\r
4315 SelectPalette(hdc, hPal, TRUE);
\r
4316 nnew = RealizePalette(hdc);
\r
4318 paletteChanged = TRUE;
\r
4319 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4321 ReleaseDC(hwnd, hdc);
\r
4325 case WM_QUERYNEWPALETTE:
\r
4326 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4328 HDC hdc = GetDC(hwndMain);
\r
4329 paletteChanged = FALSE;
\r
4330 SelectPalette(hdc, hPal, FALSE);
\r
4331 nnew = RealizePalette(hdc);
\r
4333 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4335 ReleaseDC(hwnd, hdc);
\r
4340 case WM_COMMAND: /* message: command from application menu */
\r
4341 wmId = LOWORD(wParam);
\r
4342 wmEvent = HIWORD(wParam);
\r
4347 SAY("new game enter a move to play against the computer with white");
\r
4350 case IDM_NewGameFRC:
\r
4351 if( NewGameFRC() == 0 ) {
\r
4356 case IDM_NewVariant:
\r
4357 NewVariantPopup(hwnd);
\r
4360 case IDM_LoadGame:
\r
4361 LoadGameDialog(hwnd, "Load Game from File");
\r
4364 case IDM_LoadNextGame:
\r
4368 case IDM_LoadPrevGame:
\r
4372 case IDM_ReloadGame:
\r
4376 case IDM_LoadPosition:
\r
4377 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4378 Reset(FALSE, TRUE);
\r
4381 f = OpenFileDialog(hwnd, "rb", "",
\r
4382 appData.oldSaveStyle ? "pos" : "fen",
\r
4384 "Load Position from File", &number, fileTitle, NULL);
\r
4386 LoadPosition(f, number, fileTitle);
\r
4390 case IDM_LoadNextPosition:
\r
4391 ReloadPosition(1);
\r
4394 case IDM_LoadPrevPosition:
\r
4395 ReloadPosition(-1);
\r
4398 case IDM_ReloadPosition:
\r
4399 ReloadPosition(0);
\r
4402 case IDM_SaveGame:
\r
4403 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4404 f = OpenFileDialog(hwnd, "a", defName,
\r
4405 appData.oldSaveStyle ? "gam" : "pgn",
\r
4407 "Save Game to File", NULL, fileTitle, NULL);
\r
4409 SaveGame(f, 0, "");
\r
4413 case IDM_SavePosition:
\r
4414 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4415 f = OpenFileDialog(hwnd, "a", defName,
\r
4416 appData.oldSaveStyle ? "pos" : "fen",
\r
4418 "Save Position to File", NULL, fileTitle, NULL);
\r
4420 SavePosition(f, 0, "");
\r
4424 case IDM_SaveDiagram:
\r
4425 defName = "diagram";
\r
4426 f = OpenFileDialog(hwnd, "wb", defName,
\r
4429 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4435 case IDM_CopyGame:
\r
4436 CopyGameToClipboard();
\r
4439 case IDM_PasteGame:
\r
4440 PasteGameFromClipboard();
\r
4443 case IDM_CopyGameListToClipboard:
\r
4444 CopyGameListToClipboard();
\r
4447 /* [AS] Autodetect FEN or PGN data */
\r
4448 case IDM_PasteAny:
\r
4449 PasteGameOrFENFromClipboard();
\r
4452 /* [AS] Move history */
\r
4453 case IDM_ShowMoveHistory:
\r
4454 if( MoveHistoryIsUp() ) {
\r
4455 MoveHistoryPopDown();
\r
4458 MoveHistoryPopUp();
\r
4462 /* [AS] Eval graph */
\r
4463 case IDM_ShowEvalGraph:
\r
4464 if( EvalGraphIsUp() ) {
\r
4465 EvalGraphPopDown();
\r
4469 SetFocus(hwndMain);
\r
4473 /* [AS] Engine output */
\r
4474 case IDM_ShowEngineOutput:
\r
4475 if( EngineOutputIsUp() ) {
\r
4476 EngineOutputPopDown();
\r
4479 EngineOutputPopUp();
\r
4483 /* [AS] User adjudication */
\r
4484 case IDM_UserAdjudication_White:
\r
4485 UserAdjudicationEvent( +1 );
\r
4488 case IDM_UserAdjudication_Black:
\r
4489 UserAdjudicationEvent( -1 );
\r
4492 case IDM_UserAdjudication_Draw:
\r
4493 UserAdjudicationEvent( 0 );
\r
4496 /* [AS] Game list options dialog */
\r
4497 case IDM_GameListOptions:
\r
4498 GameListOptions();
\r
4505 case IDM_CopyPosition:
\r
4506 CopyFENToClipboard();
\r
4509 case IDM_PastePosition:
\r
4510 PasteFENFromClipboard();
\r
4513 case IDM_MailMove:
\r
4517 case IDM_ReloadCMailMsg:
\r
4518 Reset(TRUE, TRUE);
\r
4519 ReloadCmailMsgEvent(FALSE);
\r
4522 case IDM_Minimize:
\r
4523 ShowWindow(hwnd, SW_MINIMIZE);
\r
4530 case IDM_MachineWhite:
\r
4531 MachineWhiteEvent();
\r
4533 * refresh the tags dialog only if it's visible
\r
4535 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4537 tags = PGNTags(&gameInfo);
\r
4538 TagsPopUp(tags, CmailMsg());
\r
4541 SAY("computer starts playing white");
\r
4544 case IDM_MachineBlack:
\r
4545 MachineBlackEvent();
\r
4547 * refresh the tags dialog only if it's visible
\r
4549 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4551 tags = PGNTags(&gameInfo);
\r
4552 TagsPopUp(tags, CmailMsg());
\r
4555 SAY("computer starts playing black");
\r
4558 case IDM_TwoMachines:
\r
4559 TwoMachinesEvent();
\r
4561 * refresh the tags dialog only if it's visible
\r
4563 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4565 tags = PGNTags(&gameInfo);
\r
4566 TagsPopUp(tags, CmailMsg());
\r
4569 SAY("computer starts playing both sides");
\r
4572 case IDM_AnalysisMode:
\r
4573 if (!first.analysisSupport) {
\r
4574 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4575 DisplayError(buf, 0);
\r
4577 SAY("analyzing current position");
\r
4578 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4579 if (appData.icsActive) {
\r
4580 if (gameMode != IcsObserving) {
\r
4581 sprintf(buf, "You are not observing a game");
\r
4582 DisplayError(buf, 0);
\r
4583 /* secure check */
\r
4584 if (appData.icsEngineAnalyze) {
\r
4585 if (appData.debugMode)
\r
4586 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4587 ExitAnalyzeMode();
\r
4593 /* if enable, user want disable icsEngineAnalyze */
\r
4594 if (appData.icsEngineAnalyze) {
\r
4595 ExitAnalyzeMode();
\r
4599 appData.icsEngineAnalyze = TRUE;
\r
4600 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4603 if (!appData.showThinking) ToggleShowThinking();
\r
4604 AnalyzeModeEvent();
\r
4608 case IDM_AnalyzeFile:
\r
4609 if (!first.analysisSupport) {
\r
4610 char buf[MSG_SIZ];
\r
4611 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4612 DisplayError(buf, 0);
\r
4614 if (!appData.showThinking) ToggleShowThinking();
\r
4615 AnalyzeFileEvent();
\r
4616 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4617 AnalysisPeriodicEvent(1);
\r
4621 case IDM_IcsClient:
\r
4625 case IDM_EditGame:
\r
4630 case IDM_EditPosition:
\r
4631 EditPositionEvent();
\r
4632 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4635 case IDM_Training:
\r
4639 case IDM_ShowGameList:
\r
4640 ShowGameListProc();
\r
4643 case IDM_EditTags:
\r
4647 case IDM_EditComment:
\r
4648 if (commentUp && editComment) {
\r
4651 EditCommentEvent();
\r
4671 case IDM_CallFlag:
\r
4691 case IDM_StopObserving:
\r
4692 StopObservingEvent();
\r
4695 case IDM_StopExamining:
\r
4696 StopExaminingEvent();
\r
4700 UploadGameEvent();
\r
4703 case IDM_TypeInMove:
\r
4704 PopUpMoveDialog('\000');
\r
4707 case IDM_TypeInName:
\r
4708 PopUpNameDialog('\000');
\r
4711 case IDM_Backward:
\r
4713 SetFocus(hwndMain);
\r
4720 SetFocus(hwndMain);
\r
4725 SetFocus(hwndMain);
\r
4730 SetFocus(hwndMain);
\r
4734 RevertEvent(FALSE);
\r
4737 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4738 RevertEvent(TRUE);
\r
4741 case IDM_TruncateGame:
\r
4742 TruncateGameEvent();
\r
4749 case IDM_RetractMove:
\r
4750 RetractMoveEvent();
\r
4753 case IDM_FlipView:
\r
4754 flipView = !flipView;
\r
4755 DrawPosition(FALSE, NULL);
\r
4758 case IDM_FlipClock:
\r
4759 flipClock = !flipClock;
\r
4760 DisplayBothClocks();
\r
4761 DrawPosition(FALSE, NULL);
\r
4764 case IDM_MuteSounds:
\r
4765 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4766 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4767 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4770 case IDM_GeneralOptions:
\r
4771 GeneralOptionsPopup(hwnd);
\r
4772 DrawPosition(TRUE, NULL);
\r
4775 case IDM_BoardOptions:
\r
4776 BoardOptionsPopup(hwnd);
\r
4779 case IDM_EnginePlayOptions:
\r
4780 EnginePlayOptionsPopup(hwnd);
\r
4783 case IDM_Engine1Options:
\r
4784 EngineOptionsPopup(hwnd, &first);
\r
4787 case IDM_Engine2Options:
\r
4788 EngineOptionsPopup(hwnd, &second);
\r
4791 case IDM_OptionsUCI:
\r
4792 UciOptionsPopup(hwnd);
\r
4795 case IDM_IcsOptions:
\r
4796 IcsOptionsPopup(hwnd);
\r
4800 FontsOptionsPopup(hwnd);
\r
4804 SoundOptionsPopup(hwnd);
\r
4807 case IDM_CommPort:
\r
4808 CommPortOptionsPopup(hwnd);
\r
4811 case IDM_LoadOptions:
\r
4812 LoadOptionsPopup(hwnd);
\r
4815 case IDM_SaveOptions:
\r
4816 SaveOptionsPopup(hwnd);
\r
4819 case IDM_TimeControl:
\r
4820 TimeControlOptionsPopup(hwnd);
\r
4823 case IDM_SaveSettings:
\r
4824 SaveSettings(settingsFileName);
\r
4827 case IDM_SaveSettingsOnExit:
\r
4828 saveSettingsOnExit = !saveSettingsOnExit;
\r
4829 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4830 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4831 MF_CHECKED : MF_UNCHECKED));
\r
4842 case IDM_AboutGame:
\r
4847 appData.debugMode = !appData.debugMode;
\r
4848 if (appData.debugMode) {
\r
4849 char dir[MSG_SIZ];
\r
4850 GetCurrentDirectory(MSG_SIZ, dir);
\r
4851 SetCurrentDirectory(installDir);
\r
4852 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4853 SetCurrentDirectory(dir);
\r
4854 setbuf(debugFP, NULL);
\r
4861 case IDM_HELPCONTENTS:
\r
4862 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4863 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4864 MessageBox (GetFocus(),
\r
4865 "Unable to activate help",
\r
4866 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4870 case IDM_HELPSEARCH:
\r
4871 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4872 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4873 MessageBox (GetFocus(),
\r
4874 "Unable to activate help",
\r
4875 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4879 case IDM_HELPHELP:
\r
4880 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4881 MessageBox (GetFocus(),
\r
4882 "Unable to activate help",
\r
4883 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4888 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4890 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4891 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4892 FreeProcInstance(lpProc);
\r
4895 case IDM_DirectCommand1:
\r
4896 AskQuestionEvent("Direct Command",
\r
4897 "Send to chess program:", "", "1");
\r
4899 case IDM_DirectCommand2:
\r
4900 AskQuestionEvent("Direct Command",
\r
4901 "Send to second chess program:", "", "2");
\r
4904 case EP_WhitePawn:
\r
4905 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4906 fromX = fromY = -1;
\r
4909 case EP_WhiteKnight:
\r
4910 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4911 fromX = fromY = -1;
\r
4914 case EP_WhiteBishop:
\r
4915 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4916 fromX = fromY = -1;
\r
4919 case EP_WhiteRook:
\r
4920 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4921 fromX = fromY = -1;
\r
4924 case EP_WhiteQueen:
\r
4925 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4926 fromX = fromY = -1;
\r
4929 case EP_WhiteFerz:
\r
4930 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4931 fromX = fromY = -1;
\r
4934 case EP_WhiteWazir:
\r
4935 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4936 fromX = fromY = -1;
\r
4939 case EP_WhiteAlfil:
\r
4940 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4941 fromX = fromY = -1;
\r
4944 case EP_WhiteCannon:
\r
4945 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4946 fromX = fromY = -1;
\r
4949 case EP_WhiteCardinal:
\r
4950 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4951 fromX = fromY = -1;
\r
4954 case EP_WhiteMarshall:
\r
4955 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4956 fromX = fromY = -1;
\r
4959 case EP_WhiteKing:
\r
4960 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4961 fromX = fromY = -1;
\r
4964 case EP_BlackPawn:
\r
4965 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4966 fromX = fromY = -1;
\r
4969 case EP_BlackKnight:
\r
4970 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4971 fromX = fromY = -1;
\r
4974 case EP_BlackBishop:
\r
4975 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4976 fromX = fromY = -1;
\r
4979 case EP_BlackRook:
\r
4980 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4981 fromX = fromY = -1;
\r
4984 case EP_BlackQueen:
\r
4985 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4986 fromX = fromY = -1;
\r
4989 case EP_BlackFerz:
\r
4990 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4991 fromX = fromY = -1;
\r
4994 case EP_BlackWazir:
\r
4995 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4996 fromX = fromY = -1;
\r
4999 case EP_BlackAlfil:
\r
5000 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5001 fromX = fromY = -1;
\r
5004 case EP_BlackCannon:
\r
5005 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5006 fromX = fromY = -1;
\r
5009 case EP_BlackCardinal:
\r
5010 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5011 fromX = fromY = -1;
\r
5014 case EP_BlackMarshall:
\r
5015 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5016 fromX = fromY = -1;
\r
5019 case EP_BlackKing:
\r
5020 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5021 fromX = fromY = -1;
\r
5024 case EP_EmptySquare:
\r
5025 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5026 fromX = fromY = -1;
\r
5029 case EP_ClearBoard:
\r
5030 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5031 fromX = fromY = -1;
\r
5035 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5036 fromX = fromY = -1;
\r
5040 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5041 fromX = fromY = -1;
\r
5045 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5046 fromX = fromY = -1;
\r
5050 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5051 fromX = fromY = -1;
\r
5055 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5056 fromX = fromY = -1;
\r
5060 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5061 fromX = fromY = -1;
\r
5065 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5066 fromX = fromY = -1;
\r
5070 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5071 fromX = fromY = -1;
\r
5075 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5076 fromX = fromY = -1;
\r
5080 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5086 case CLOCK_TIMER_ID:
\r
5087 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5088 clockTimerEvent = 0;
\r
5089 DecrementClocks(); /* call into back end */
\r
5091 case LOAD_GAME_TIMER_ID:
\r
5092 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5093 loadGameTimerEvent = 0;
\r
5094 AutoPlayGameLoop(); /* call into back end */
\r
5096 case ANALYSIS_TIMER_ID:
\r
5097 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5098 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5099 AnalysisPeriodicEvent(0);
\r
5101 KillTimer(hwnd, analysisTimerEvent);
\r
5102 analysisTimerEvent = 0;
\r
5105 case DELAYED_TIMER_ID:
\r
5106 KillTimer(hwnd, delayedTimerEvent);
\r
5107 delayedTimerEvent = 0;
\r
5108 delayedTimerCallback();
\r
5113 case WM_USER_Input:
\r
5114 InputEvent(hwnd, message, wParam, lParam);
\r
5117 /* [AS] Also move "attached" child windows */
\r
5118 case WM_WINDOWPOSCHANGING:
\r
5120 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5121 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5123 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5124 /* Window is moving */
\r
5127 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5128 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5129 rcMain.right = wpMain.x + wpMain.width;
\r
5130 rcMain.top = wpMain.y;
\r
5131 rcMain.bottom = wpMain.y + wpMain.height;
\r
5133 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5134 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5135 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5136 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5137 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5138 wpMain.x = lpwp->x;
\r
5139 wpMain.y = lpwp->y;
\r
5144 /* [AS] Snapping */
\r
5145 case WM_ENTERSIZEMOVE:
\r
5146 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5147 if (hwnd == hwndMain) {
\r
5148 doingSizing = TRUE;
\r
5151 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5155 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5156 if (hwnd == hwndMain) {
\r
5157 lastSizing = wParam;
\r
5162 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5163 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5165 case WM_EXITSIZEMOVE:
\r
5166 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5167 if (hwnd == hwndMain) {
\r
5169 doingSizing = FALSE;
\r
5170 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5171 GetClientRect(hwnd, &client);
\r
5172 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5174 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5176 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5179 case WM_DESTROY: /* message: window being destroyed */
\r
5180 PostQuitMessage(0);
\r
5184 if (hwnd == hwndMain) {
\r
5189 default: /* Passes it on if unprocessed */
\r
5190 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5195 /*---------------------------------------------------------------------------*\
\r
5197 * Misc utility routines
\r
5199 \*---------------------------------------------------------------------------*/
\r
5202 * Decent random number generator, at least not as bad as Windows
\r
5203 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5205 unsigned int randstate;
\r
5210 randstate = randstate * 1664525 + 1013904223;
\r
5211 return (int) randstate & 0x7fffffff;
\r
5215 mysrandom(unsigned int seed)
\r
5222 * returns TRUE if user selects a different color, FALSE otherwise
\r
5226 ChangeColor(HWND hwnd, COLORREF *which)
\r
5228 static BOOL firstTime = TRUE;
\r
5229 static DWORD customColors[16];
\r
5231 COLORREF newcolor;
\r
5236 /* Make initial colors in use available as custom colors */
\r
5237 /* Should we put the compiled-in defaults here instead? */
\r
5239 customColors[i++] = lightSquareColor & 0xffffff;
\r
5240 customColors[i++] = darkSquareColor & 0xffffff;
\r
5241 customColors[i++] = whitePieceColor & 0xffffff;
\r
5242 customColors[i++] = blackPieceColor & 0xffffff;
\r
5243 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5244 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5246 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5247 customColors[i++] = textAttribs[ccl].color;
\r
5249 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5250 firstTime = FALSE;
\r
5253 cc.lStructSize = sizeof(cc);
\r
5254 cc.hwndOwner = hwnd;
\r
5255 cc.hInstance = NULL;
\r
5256 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5257 cc.lpCustColors = (LPDWORD) customColors;
\r
5258 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5260 if (!ChooseColor(&cc)) return FALSE;
\r
5262 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5263 if (newcolor == *which) return FALSE;
\r
5264 *which = newcolor;
\r
5268 InitDrawingColors();
\r
5269 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5274 MyLoadSound(MySound *ms)
\r
5280 if (ms->data) free(ms->data);
\r
5283 switch (ms->name[0]) {
\r
5289 /* System sound from Control Panel. Don't preload here. */
\r
5293 if (ms->name[1] == NULLCHAR) {
\r
5294 /* "!" alone = silence */
\r
5297 /* Builtin wave resource. Error if not found. */
\r
5298 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5299 if (h == NULL) break;
\r
5300 ms->data = (void *)LoadResource(hInst, h);
\r
5301 if (h == NULL) break;
\r
5306 /* .wav file. Error if not found. */
\r
5307 f = fopen(ms->name, "rb");
\r
5308 if (f == NULL) break;
\r
5309 if (fstat(fileno(f), &st) < 0) break;
\r
5310 ms->data = malloc(st.st_size);
\r
5311 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5317 char buf[MSG_SIZ];
\r
5318 sprintf(buf, "Error loading sound %s", ms->name);
\r
5319 DisplayError(buf, GetLastError());
\r
5325 MyPlaySound(MySound *ms)
\r
5327 BOOLEAN ok = FALSE;
\r
5329 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5330 switch (ms->name[0]) {
\r
5332 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5337 /* System sound from Control Panel (deprecated feature).
\r
5338 "$" alone or an unset sound name gets default beep (still in use). */
\r
5339 if (ms->name[1]) {
\r
5340 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5342 if (!ok) ok = MessageBeep(MB_OK);
\r
5345 /* Builtin wave resource, or "!" alone for silence */
\r
5346 if (ms->name[1]) {
\r
5347 if (ms->data == NULL) return FALSE;
\r
5348 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5354 /* .wav file. Error if not found. */
\r
5355 if (ms->data == NULL) return FALSE;
\r
5356 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5359 /* Don't print an error: this can happen innocently if the sound driver
\r
5360 is busy; for instance, if another instance of WinBoard is playing
\r
5361 a sound at about the same time. */
\r
5367 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5370 OPENFILENAME *ofn;
\r
5371 static UINT *number; /* gross that this is static */
\r
5373 switch (message) {
\r
5374 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5375 /* Center the dialog over the application window */
\r
5376 ofn = (OPENFILENAME *) lParam;
\r
5377 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5378 number = (UINT *) ofn->lCustData;
\r
5379 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5383 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5384 return FALSE; /* Allow for further processing */
\r
5387 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5388 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5390 return FALSE; /* Allow for further processing */
\r
5396 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5398 static UINT *number;
\r
5399 OPENFILENAME *ofname;
\r
5402 case WM_INITDIALOG:
\r
5403 ofname = (OPENFILENAME *)lParam;
\r
5404 number = (UINT *)(ofname->lCustData);
\r
5407 ofnot = (OFNOTIFY *)lParam;
\r
5408 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5409 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5418 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5419 char *nameFilt, char *dlgTitle, UINT *number,
\r
5420 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5422 OPENFILENAME openFileName;
\r
5423 char buf1[MSG_SIZ];
\r
5426 if (fileName == NULL) fileName = buf1;
\r
5427 if (defName == NULL) {
\r
5428 strcpy(fileName, "*.");
\r
5429 strcat(fileName, defExt);
\r
5431 strcpy(fileName, defName);
\r
5433 if (fileTitle) strcpy(fileTitle, "");
\r
5434 if (number) *number = 0;
\r
5436 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5437 openFileName.hwndOwner = hwnd;
\r
5438 openFileName.hInstance = (HANDLE) hInst;
\r
5439 openFileName.lpstrFilter = nameFilt;
\r
5440 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5441 openFileName.nMaxCustFilter = 0L;
\r
5442 openFileName.nFilterIndex = 1L;
\r
5443 openFileName.lpstrFile = fileName;
\r
5444 openFileName.nMaxFile = MSG_SIZ;
\r
5445 openFileName.lpstrFileTitle = fileTitle;
\r
5446 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5447 openFileName.lpstrInitialDir = NULL;
\r
5448 openFileName.lpstrTitle = dlgTitle;
\r
5449 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5450 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5451 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5452 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5453 openFileName.nFileOffset = 0;
\r
5454 openFileName.nFileExtension = 0;
\r
5455 openFileName.lpstrDefExt = defExt;
\r
5456 openFileName.lCustData = (LONG) number;
\r
5457 openFileName.lpfnHook = oldDialog ?
\r
5458 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5459 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5461 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5462 GetOpenFileName(&openFileName)) {
\r
5463 /* open the file */
\r
5464 f = fopen(openFileName.lpstrFile, write);
\r
5466 MessageBox(hwnd, "File open failed", NULL,
\r
5467 MB_OK|MB_ICONEXCLAMATION);
\r
5471 int err = CommDlgExtendedError();
\r
5472 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5481 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5483 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5486 * Get the first pop-up menu in the menu template. This is the
\r
5487 * menu that TrackPopupMenu displays.
\r
5489 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5491 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5494 * TrackPopup uses screen coordinates, so convert the
\r
5495 * coordinates of the mouse click to screen coordinates.
\r
5497 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5499 /* Draw and track the floating pop-up menu. */
\r
5500 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5501 pt.x, pt.y, 0, hwnd, NULL);
\r
5503 /* Destroy the menu.*/
\r
5504 DestroyMenu(hmenu);
\r
5509 int sizeX, sizeY, newSizeX, newSizeY;
\r
5511 } ResizeEditPlusButtonsClosure;
\r
5514 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5516 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5520 if (hChild == cl->hText) return TRUE;
\r
5521 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5522 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5523 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5524 ScreenToClient(cl->hDlg, &pt);
\r
5525 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5526 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5530 /* Resize a dialog that has a (rich) edit field filling most of
\r
5531 the top, with a row of buttons below */
\r
5533 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5536 int newTextHeight, newTextWidth;
\r
5537 ResizeEditPlusButtonsClosure cl;
\r
5539 /*if (IsIconic(hDlg)) return;*/
\r
5540 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5542 cl.hdwp = BeginDeferWindowPos(8);
\r
5544 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5545 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5546 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5547 if (newTextHeight < 0) {
\r
5548 newSizeY += -newTextHeight;
\r
5549 newTextHeight = 0;
\r
5551 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5552 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5558 cl.newSizeX = newSizeX;
\r
5559 cl.newSizeY = newSizeY;
\r
5560 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5562 EndDeferWindowPos(cl.hdwp);
\r
5565 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5567 RECT rChild, rParent;
\r
5568 int wChild, hChild, wParent, hParent;
\r
5569 int wScreen, hScreen, xNew, yNew;
\r
5572 /* Get the Height and Width of the child window */
\r
5573 GetWindowRect (hwndChild, &rChild);
\r
5574 wChild = rChild.right - rChild.left;
\r
5575 hChild = rChild.bottom - rChild.top;
\r
5577 /* Get the Height and Width of the parent window */
\r
5578 GetWindowRect (hwndParent, &rParent);
\r
5579 wParent = rParent.right - rParent.left;
\r
5580 hParent = rParent.bottom - rParent.top;
\r
5582 /* Get the display limits */
\r
5583 hdc = GetDC (hwndChild);
\r
5584 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5585 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5586 ReleaseDC(hwndChild, hdc);
\r
5588 /* Calculate new X position, then adjust for screen */
\r
5589 xNew = rParent.left + ((wParent - wChild) /2);
\r
5592 } else if ((xNew+wChild) > wScreen) {
\r
5593 xNew = wScreen - wChild;
\r
5596 /* Calculate new Y position, then adjust for screen */
\r
5598 yNew = rParent.top + ((hParent - hChild) /2);
\r
5601 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5606 } else if ((yNew+hChild) > hScreen) {
\r
5607 yNew = hScreen - hChild;
\r
5610 /* Set it, and return */
\r
5611 return SetWindowPos (hwndChild, NULL,
\r
5612 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5615 /* Center one window over another */
\r
5616 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5618 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5621 /*---------------------------------------------------------------------------*\
\r
5623 * Startup Dialog functions
\r
5625 \*---------------------------------------------------------------------------*/
\r
5627 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5629 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5631 while (*cd != NULL) {
\r
5632 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5638 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5640 char buf1[MAX_ARG_LEN];
\r
5643 if (str[0] == '@') {
\r
5644 FILE* f = fopen(str + 1, "r");
\r
5646 DisplayFatalError(str + 1, errno, 2);
\r
5649 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5651 buf1[len] = NULLCHAR;
\r
5655 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5658 char buf[MSG_SIZ];
\r
5659 char *end = strchr(str, '\n');
\r
5660 if (end == NULL) return;
\r
5661 memcpy(buf, str, end - str);
\r
5662 buf[end - str] = NULLCHAR;
\r
5663 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5669 SetStartupDialogEnables(HWND hDlg)
\r
5671 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5672 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5673 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5674 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5675 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5676 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5677 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5678 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5679 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5680 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5681 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5682 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5683 IsDlgButtonChecked(hDlg, OPT_View));
\r
5687 QuoteForFilename(char *filename)
\r
5689 int dquote, space;
\r
5690 dquote = strchr(filename, '"') != NULL;
\r
5691 space = strchr(filename, ' ') != NULL;
\r
5692 if (dquote || space) {
\r
5704 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5706 char buf[MSG_SIZ];
\r
5709 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5710 q = QuoteForFilename(nthcp);
\r
5711 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5712 if (*nthdir != NULLCHAR) {
\r
5713 q = QuoteForFilename(nthdir);
\r
5714 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5716 if (*nthcp == NULLCHAR) {
\r
5717 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5718 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5719 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5720 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5725 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5727 char buf[MSG_SIZ];
\r
5731 switch (message) {
\r
5732 case WM_INITDIALOG:
\r
5733 /* Center the dialog */
\r
5734 CenterWindow (hDlg, GetDesktopWindow());
\r
5735 /* Initialize the dialog items */
\r
5736 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5737 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5738 firstChessProgramNames);
\r
5739 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5740 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5741 secondChessProgramNames);
\r
5742 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5743 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5744 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5745 if (*appData.icsHelper != NULLCHAR) {
\r
5746 char *q = QuoteForFilename(appData.icsHelper);
\r
5747 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5749 if (*appData.icsHost == NULLCHAR) {
\r
5750 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5751 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5752 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5753 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5754 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5757 if (appData.icsActive) {
\r
5758 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5760 else if (appData.noChessProgram) {
\r
5761 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5764 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5767 SetStartupDialogEnables(hDlg);
\r
5771 switch (LOWORD(wParam)) {
\r
5773 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5774 strcpy(buf, "/fcp=");
\r
5775 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5777 ParseArgs(StringGet, &p);
\r
5778 strcpy(buf, "/scp=");
\r
5779 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5781 ParseArgs(StringGet, &p);
\r
5782 appData.noChessProgram = FALSE;
\r
5783 appData.icsActive = FALSE;
\r
5784 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5785 strcpy(buf, "/ics /icshost=");
\r
5786 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5788 ParseArgs(StringGet, &p);
\r
5789 if (appData.zippyPlay) {
\r
5790 strcpy(buf, "/fcp=");
\r
5791 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5793 ParseArgs(StringGet, &p);
\r
5795 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5796 appData.noChessProgram = TRUE;
\r
5797 appData.icsActive = FALSE;
\r
5799 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5800 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5803 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5804 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5806 ParseArgs(StringGet, &p);
\r
5808 EndDialog(hDlg, TRUE);
\r
5815 case IDM_HELPCONTENTS:
\r
5816 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5817 MessageBox (GetFocus(),
\r
5818 "Unable to activate help",
\r
5819 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5824 SetStartupDialogEnables(hDlg);
\r
5832 /*---------------------------------------------------------------------------*\
\r
5834 * About box dialog functions
\r
5836 \*---------------------------------------------------------------------------*/
\r
5838 /* Process messages for "About" dialog box */
\r
5840 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5842 switch (message) {
\r
5843 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5844 /* Center the dialog over the application window */
\r
5845 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5846 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5850 case WM_COMMAND: /* message: received a command */
\r
5851 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5852 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5853 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5861 /*---------------------------------------------------------------------------*\
\r
5863 * Comment Dialog functions
\r
5865 \*---------------------------------------------------------------------------*/
\r
5868 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5870 static HANDLE hwndText = NULL;
\r
5871 int len, newSizeX, newSizeY, flags;
\r
5872 static int sizeX, sizeY;
\r
5877 switch (message) {
\r
5878 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5879 /* Initialize the dialog items */
\r
5880 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5881 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5882 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5883 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5884 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5885 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5886 SetWindowText(hDlg, commentTitle);
\r
5887 if (editComment) {
\r
5888 SetFocus(hwndText);
\r
5890 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5892 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5893 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5894 MAKELPARAM(FALSE, 0));
\r
5895 /* Size and position the dialog */
\r
5896 if (!commentDialog) {
\r
5897 commentDialog = hDlg;
\r
5898 flags = SWP_NOZORDER;
\r
5899 GetClientRect(hDlg, &rect);
\r
5900 sizeX = rect.right;
\r
5901 sizeY = rect.bottom;
\r
5902 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5903 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5904 WINDOWPLACEMENT wp;
\r
5905 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5906 wp.length = sizeof(WINDOWPLACEMENT);
\r
5908 wp.showCmd = SW_SHOW;
\r
5909 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5910 wp.rcNormalPosition.left = wpComment.x;
\r
5911 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5912 wp.rcNormalPosition.top = wpComment.y;
\r
5913 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5914 SetWindowPlacement(hDlg, &wp);
\r
5916 GetClientRect(hDlg, &rect);
\r
5917 newSizeX = rect.right;
\r
5918 newSizeY = rect.bottom;
\r
5919 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5920 newSizeX, newSizeY);
\r
5925 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5928 case WM_COMMAND: /* message: received a command */
\r
5929 switch (LOWORD(wParam)) {
\r
5931 if (editComment) {
\r
5933 /* Read changed options from the dialog box */
\r
5934 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5935 len = GetWindowTextLength(hwndText);
\r
5936 str = (char *) malloc(len + 1);
\r
5937 GetWindowText(hwndText, str, len + 1);
\r
5946 ReplaceComment(commentIndex, str);
\r
5953 case OPT_CancelComment:
\r
5957 case OPT_ClearComment:
\r
5958 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5961 case OPT_EditComment:
\r
5962 EditCommentEvent();
\r
5970 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5971 if( wParam == OPT_CommentText ) {
\r
5972 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5974 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5978 pt.x = LOWORD( lpMF->lParam );
\r
5979 pt.y = HIWORD( lpMF->lParam );
\r
5981 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5983 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5984 len = GetWindowTextLength(hwndText);
\r
5985 str = (char *) malloc(len + 1);
\r
5986 GetWindowText(hwndText, str, len + 1);
\r
5987 ReplaceComment(commentIndex, str);
\r
5988 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5989 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5992 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5993 lpMF->msg = WM_USER;
\r
6001 newSizeX = LOWORD(lParam);
\r
6002 newSizeY = HIWORD(lParam);
\r
6003 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6008 case WM_GETMINMAXINFO:
\r
6009 /* Prevent resizing window too small */
\r
6010 mmi = (MINMAXINFO *) lParam;
\r
6011 mmi->ptMinTrackSize.x = 100;
\r
6012 mmi->ptMinTrackSize.y = 100;
\r
6019 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6024 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6026 if (str == NULL) str = "";
\r
6027 p = (char *) malloc(2 * strlen(str) + 2);
\r
6030 if (*str == '\n') *q++ = '\r';
\r
6034 if (commentText != NULL) free(commentText);
\r
6036 commentIndex = index;
\r
6037 commentTitle = title;
\r
6039 editComment = edit;
\r
6041 if (commentDialog) {
\r
6042 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6043 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6045 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6046 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6047 hwndMain, (DLGPROC)lpProc);
\r
6048 FreeProcInstance(lpProc);
\r
6054 /*---------------------------------------------------------------------------*\
\r
6056 * Type-in move dialog functions
\r
6058 \*---------------------------------------------------------------------------*/
\r
6061 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6063 char move[MSG_SIZ];
\r
6065 ChessMove moveType;
\r
6066 int fromX, fromY, toX, toY;
\r
6069 switch (message) {
\r
6070 case WM_INITDIALOG:
\r
6071 move[0] = (char) lParam;
\r
6072 move[1] = NULLCHAR;
\r
6073 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6074 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6075 SetWindowText(hInput, move);
\r
6077 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6081 switch (LOWORD(wParam)) {
\r
6083 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6084 { int n; Board board;
\r
6086 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6087 EditPositionPasteFEN(move);
\r
6088 EndDialog(hDlg, TRUE);
\r
6091 // [HGM] movenum: allow move number to be typed in any mode
\r
6092 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6094 EndDialog(hDlg, TRUE);
\r
6098 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6099 gameMode != Training) {
\r
6100 DisplayMoveError("Displayed move is not current");
\r
6102 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6103 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6104 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6105 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6106 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6107 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6108 if (gameMode != Training)
\r
6109 forwardMostMove = currentMove;
\r
6110 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6112 DisplayMoveError("Could not parse move");
\r
6115 EndDialog(hDlg, TRUE);
\r
6118 EndDialog(hDlg, FALSE);
\r
6129 PopUpMoveDialog(char firstchar)
\r
6133 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6134 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6135 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6136 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6137 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6138 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6139 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6140 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6141 gameMode == Training) {
\r
6142 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6143 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6144 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6145 FreeProcInstance(lpProc);
\r
6149 /*---------------------------------------------------------------------------*\
\r
6151 * Type-in name dialog functions
\r
6153 \*---------------------------------------------------------------------------*/
\r
6156 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6158 char move[MSG_SIZ];
\r
6161 switch (message) {
\r
6162 case WM_INITDIALOG:
\r
6163 move[0] = (char) lParam;
\r
6164 move[1] = NULLCHAR;
\r
6165 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6166 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6167 SetWindowText(hInput, move);
\r
6169 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6173 switch (LOWORD(wParam)) {
\r
6175 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6176 appData.userName = strdup(move);
\r
6179 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6180 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6181 DisplayTitle(move);
\r
6185 EndDialog(hDlg, TRUE);
\r
6188 EndDialog(hDlg, FALSE);
\r
6199 PopUpNameDialog(char firstchar)
\r
6203 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6204 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6205 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6206 FreeProcInstance(lpProc);
\r
6209 /*---------------------------------------------------------------------------*\
\r
6213 \*---------------------------------------------------------------------------*/
\r
6215 /* Nonmodal error box */
\r
6216 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6217 WPARAM wParam, LPARAM lParam);
\r
6220 ErrorPopUp(char *title, char *content)
\r
6224 BOOLEAN modal = hwndMain == NULL;
\r
6242 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6243 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6246 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6248 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6249 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6250 hwndMain, (DLGPROC)lpProc);
\r
6251 FreeProcInstance(lpProc);
\r
6258 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6259 if (errorDialog == NULL) return;
\r
6260 DestroyWindow(errorDialog);
\r
6261 errorDialog = NULL;
\r
6262 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6266 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6271 switch (message) {
\r
6272 case WM_INITDIALOG:
\r
6273 GetWindowRect(hDlg, &rChild);
\r
6276 SetWindowPos(hDlg, NULL, rChild.left,
\r
6277 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6278 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6282 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6283 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6284 and it doesn't work when you resize the dialog.
\r
6285 For now, just give it a default position.
\r
6287 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6289 errorDialog = hDlg;
\r
6290 SetWindowText(hDlg, errorTitle);
\r
6291 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6292 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6296 switch (LOWORD(wParam)) {
\r
6299 if (errorDialog == hDlg) errorDialog = NULL;
\r
6300 DestroyWindow(hDlg);
\r
6312 HWND gothicDialog = NULL;
\r
6315 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6319 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6321 switch (message) {
\r
6322 case WM_INITDIALOG:
\r
6323 GetWindowRect(hDlg, &rChild);
\r
6325 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6329 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6330 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6331 and it doesn't work when you resize the dialog.
\r
6332 For now, just give it a default position.
\r
6334 gothicDialog = hDlg;
\r
6335 SetWindowText(hDlg, errorTitle);
\r
6336 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6337 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6341 switch (LOWORD(wParam)) {
\r
6344 if (errorDialog == hDlg) errorDialog = NULL;
\r
6345 DestroyWindow(hDlg);
\r
6357 GothicPopUp(char *title, VariantClass variant)
\r
6360 static char *lastTitle;
\r
6362 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6363 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6365 if(lastTitle != title && gothicDialog != NULL) {
\r
6366 DestroyWindow(gothicDialog);
\r
6367 gothicDialog = NULL;
\r
6369 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6370 title = lastTitle;
\r
6371 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6372 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6373 hwndMain, (DLGPROC)lpProc);
\r
6374 FreeProcInstance(lpProc);
\r
6379 /*---------------------------------------------------------------------------*\
\r
6381 * Ics Interaction console functions
\r
6383 \*---------------------------------------------------------------------------*/
\r
6385 #define HISTORY_SIZE 64
\r
6386 static char *history[HISTORY_SIZE];
\r
6387 int histIn = 0, histP = 0;
\r
6390 SaveInHistory(char *cmd)
\r
6392 if (history[histIn] != NULL) {
\r
6393 free(history[histIn]);
\r
6394 history[histIn] = NULL;
\r
6396 if (*cmd == NULLCHAR) return;
\r
6397 history[histIn] = StrSave(cmd);
\r
6398 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6399 if (history[histIn] != NULL) {
\r
6400 free(history[histIn]);
\r
6401 history[histIn] = NULL;
\r
6407 PrevInHistory(char *cmd)
\r
6410 if (histP == histIn) {
\r
6411 if (history[histIn] != NULL) free(history[histIn]);
\r
6412 history[histIn] = StrSave(cmd);
\r
6414 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6415 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6417 return history[histP];
\r
6423 if (histP == histIn) return NULL;
\r
6424 histP = (histP + 1) % HISTORY_SIZE;
\r
6425 return history[histP];
\r
6429 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6433 hmenu = LoadMenu(hInst, "TextMenu");
\r
6434 h = GetSubMenu(hmenu, 0);
\r
6436 if (strcmp(e->item, "-") == 0) {
\r
6437 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6438 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6439 int flags = MF_STRING, j = 0;
\r
6440 if (e->item[0] == '|') {
\r
6441 flags |= MF_MENUBARBREAK;
\r
6444 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6445 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6453 WNDPROC consoleTextWindowProc;
\r
6456 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6458 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6459 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6463 SetWindowText(hInput, command);
\r
6465 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6467 sel.cpMin = 999999;
\r
6468 sel.cpMax = 999999;
\r
6469 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6474 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6475 if (sel.cpMin == sel.cpMax) {
\r
6476 /* Expand to surrounding word */
\r
6479 tr.chrg.cpMax = sel.cpMin;
\r
6480 tr.chrg.cpMin = --sel.cpMin;
\r
6481 if (sel.cpMin < 0) break;
\r
6482 tr.lpstrText = name;
\r
6483 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6484 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6488 tr.chrg.cpMin = sel.cpMax;
\r
6489 tr.chrg.cpMax = ++sel.cpMax;
\r
6490 tr.lpstrText = name;
\r
6491 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6492 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6495 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6496 MessageBeep(MB_ICONEXCLAMATION);
\r
6500 tr.lpstrText = name;
\r
6501 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6503 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6504 MessageBeep(MB_ICONEXCLAMATION);
\r
6507 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6510 sprintf(buf, "%s %s", command, name);
\r
6511 SetWindowText(hInput, buf);
\r
6512 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6514 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6515 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6516 SetWindowText(hInput, buf);
\r
6517 sel.cpMin = 999999;
\r
6518 sel.cpMax = 999999;
\r
6519 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6525 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6530 switch (message) {
\r
6532 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6535 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6538 sel.cpMin = 999999;
\r
6539 sel.cpMax = 999999;
\r
6540 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6541 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6546 if(wParam != '\022') {
\r
6547 if (wParam == '\t') {
\r
6548 if (GetKeyState(VK_SHIFT) < 0) {
\r
6550 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6551 if (buttonDesc[0].hwnd) {
\r
6552 SetFocus(buttonDesc[0].hwnd);
\r
6554 SetFocus(hwndMain);
\r
6558 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6561 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6562 JAWS_DELETE( SetFocus(hInput); )
\r
6563 SendMessage(hInput, message, wParam, lParam);
\r
6566 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6567 case WM_RBUTTONDOWN:
\r
6568 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6569 /* Move selection here if it was empty */
\r
6571 pt.x = LOWORD(lParam);
\r
6572 pt.y = HIWORD(lParam);
\r
6573 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6574 if (sel.cpMin == sel.cpMax) {
\r
6575 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6576 sel.cpMax = sel.cpMin;
\r
6577 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6579 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6580 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6582 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6583 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6584 if (sel.cpMin == sel.cpMax) {
\r
6585 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6586 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6588 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6589 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6591 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6592 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6593 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6594 MenuPopup(hwnd, pt, hmenu, -1);
\r
6598 case WM_RBUTTONUP:
\r
6599 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6600 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6601 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6605 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6607 return SendMessage(hInput, message, wParam, lParam);
\r
6608 case WM_MBUTTONDOWN:
\r
6609 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6611 switch (LOWORD(wParam)) {
\r
6612 case IDM_QuickPaste:
\r
6614 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6615 if (sel.cpMin == sel.cpMax) {
\r
6616 MessageBeep(MB_ICONEXCLAMATION);
\r
6619 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6620 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6621 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6626 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6629 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6632 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6636 int i = LOWORD(wParam) - IDM_CommandX;
\r
6637 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6638 icsTextMenuEntry[i].command != NULL) {
\r
6639 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6640 icsTextMenuEntry[i].getname,
\r
6641 icsTextMenuEntry[i].immediate);
\r
6649 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6652 WNDPROC consoleInputWindowProc;
\r
6655 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6657 char buf[MSG_SIZ];
\r
6659 static BOOL sendNextChar = FALSE;
\r
6660 static BOOL quoteNextChar = FALSE;
\r
6661 InputSource *is = consoleInputSource;
\r
6665 switch (message) {
\r
6667 if (!appData.localLineEditing || sendNextChar) {
\r
6668 is->buf[0] = (CHAR) wParam;
\r
6670 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6671 sendNextChar = FALSE;
\r
6674 if (quoteNextChar) {
\r
6675 buf[0] = (char) wParam;
\r
6676 buf[1] = NULLCHAR;
\r
6677 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6678 quoteNextChar = FALSE;
\r
6682 case '\r': /* Enter key */
\r
6683 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6684 if (consoleEcho) SaveInHistory(is->buf);
\r
6685 is->buf[is->count++] = '\n';
\r
6686 is->buf[is->count] = NULLCHAR;
\r
6687 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6688 if (consoleEcho) {
\r
6689 ConsoleOutput(is->buf, is->count, TRUE);
\r
6690 } else if (appData.localLineEditing) {
\r
6691 ConsoleOutput("\n", 1, TRUE);
\r
6694 case '\033': /* Escape key */
\r
6695 SetWindowText(hwnd, "");
\r
6696 cf.cbSize = sizeof(CHARFORMAT);
\r
6697 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6698 if (consoleEcho) {
\r
6699 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6701 cf.crTextColor = COLOR_ECHOOFF;
\r
6703 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6704 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6706 case '\t': /* Tab key */
\r
6707 if (GetKeyState(VK_SHIFT) < 0) {
\r
6709 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6712 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6713 if (buttonDesc[0].hwnd) {
\r
6714 SetFocus(buttonDesc[0].hwnd);
\r
6716 SetFocus(hwndMain);
\r
6720 case '\023': /* Ctrl+S */
\r
6721 sendNextChar = TRUE;
\r
6723 case '\021': /* Ctrl+Q */
\r
6724 quoteNextChar = TRUE;
\r
6734 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6735 p = PrevInHistory(buf);
\r
6737 SetWindowText(hwnd, p);
\r
6738 sel.cpMin = 999999;
\r
6739 sel.cpMax = 999999;
\r
6740 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6745 p = NextInHistory();
\r
6747 SetWindowText(hwnd, p);
\r
6748 sel.cpMin = 999999;
\r
6749 sel.cpMax = 999999;
\r
6750 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6756 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6760 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6764 case WM_MBUTTONDOWN:
\r
6765 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6766 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6768 case WM_RBUTTONUP:
\r
6769 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6770 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6771 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6775 hmenu = LoadMenu(hInst, "InputMenu");
\r
6776 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6777 if (sel.cpMin == sel.cpMax) {
\r
6778 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6779 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6781 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6782 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6784 pt.x = LOWORD(lParam);
\r
6785 pt.y = HIWORD(lParam);
\r
6786 MenuPopup(hwnd, pt, hmenu, -1);
\r
6790 switch (LOWORD(wParam)) {
\r
6792 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6794 case IDM_SelectAll:
\r
6796 sel.cpMax = -1; /*999999?*/
\r
6797 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6800 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6803 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6806 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6811 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6814 #define CO_MAX 100000
\r
6815 #define CO_TRIM 1000
\r
6818 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6820 static SnapData sd;
\r
6821 HWND hText, hInput;
\r
6823 static int sizeX, sizeY;
\r
6824 int newSizeX, newSizeY;
\r
6828 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6829 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6831 switch (message) {
\r
6833 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6835 ENLINK *pLink = (ENLINK*)lParam;
\r
6836 if (pLink->msg == WM_LBUTTONUP)
\r
6840 tr.chrg = pLink->chrg;
\r
6841 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6842 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6843 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6844 free(tr.lpstrText);
\r
6848 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6849 hwndConsole = hDlg;
\r
6851 consoleTextWindowProc = (WNDPROC)
\r
6852 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6853 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6854 consoleInputWindowProc = (WNDPROC)
\r
6855 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6856 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6857 Colorize(ColorNormal, TRUE);
\r
6858 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6859 ChangedConsoleFont();
\r
6860 GetClientRect(hDlg, &rect);
\r
6861 sizeX = rect.right;
\r
6862 sizeY = rect.bottom;
\r
6863 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6864 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6865 WINDOWPLACEMENT wp;
\r
6866 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6867 wp.length = sizeof(WINDOWPLACEMENT);
\r
6869 wp.showCmd = SW_SHOW;
\r
6870 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6871 wp.rcNormalPosition.left = wpConsole.x;
\r
6872 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6873 wp.rcNormalPosition.top = wpConsole.y;
\r
6874 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6875 SetWindowPlacement(hDlg, &wp);
\r
6878 // [HGM] Chessknight's change 2004-07-13
\r
6879 else { /* Determine Defaults */
\r
6880 WINDOWPLACEMENT wp;
\r
6881 wpConsole.x = wpMain.width + 1;
\r
6882 wpConsole.y = wpMain.y;
\r
6883 wpConsole.width = screenWidth - wpMain.width;
\r
6884 wpConsole.height = wpMain.height;
\r
6885 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6886 wp.length = sizeof(WINDOWPLACEMENT);
\r
6888 wp.showCmd = SW_SHOW;
\r
6889 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6890 wp.rcNormalPosition.left = wpConsole.x;
\r
6891 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6892 wp.rcNormalPosition.top = wpConsole.y;
\r
6893 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6894 SetWindowPlacement(hDlg, &wp);
\r
6897 // Allow hText to highlight URLs and send notifications on them
\r
6898 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6899 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6900 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6901 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6915 if (IsIconic(hDlg)) break;
\r
6916 newSizeX = LOWORD(lParam);
\r
6917 newSizeY = HIWORD(lParam);
\r
6918 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6919 RECT rectText, rectInput;
\r
6921 int newTextHeight, newTextWidth;
\r
6922 GetWindowRect(hText, &rectText);
\r
6923 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6924 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6925 if (newTextHeight < 0) {
\r
6926 newSizeY += -newTextHeight;
\r
6927 newTextHeight = 0;
\r
6929 SetWindowPos(hText, NULL, 0, 0,
\r
6930 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6931 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6932 pt.x = rectInput.left;
\r
6933 pt.y = rectInput.top + newSizeY - sizeY;
\r
6934 ScreenToClient(hDlg, &pt);
\r
6935 SetWindowPos(hInput, NULL,
\r
6936 pt.x, pt.y, /* needs client coords */
\r
6937 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6938 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6944 case WM_GETMINMAXINFO:
\r
6945 /* Prevent resizing window too small */
\r
6946 mmi = (MINMAXINFO *) lParam;
\r
6947 mmi->ptMinTrackSize.x = 100;
\r
6948 mmi->ptMinTrackSize.y = 100;
\r
6951 /* [AS] Snapping */
\r
6952 case WM_ENTERSIZEMOVE:
\r
6953 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6956 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6959 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6961 case WM_EXITSIZEMOVE:
\r
6962 UpdateICSWidth(hText);
\r
6963 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6966 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6974 if (hwndConsole) return;
\r
6975 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6976 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6981 ConsoleOutput(char* data, int length, int forceVisible)
\r
6986 char buf[CO_MAX+1];
\r
6989 static int delayLF = 0;
\r
6990 CHARRANGE savesel, sel;
\r
6992 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7000 while (length--) {
\r
7008 } else if (*p == '\007') {
\r
7009 MyPlaySound(&sounds[(int)SoundBell]);
\r
7016 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7017 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7018 /* Save current selection */
\r
7019 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7020 exlen = GetWindowTextLength(hText);
\r
7021 /* Find out whether current end of text is visible */
\r
7022 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7023 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7024 /* Trim existing text if it's too long */
\r
7025 if (exlen + (q - buf) > CO_MAX) {
\r
7026 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7029 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7030 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7032 savesel.cpMin -= trim;
\r
7033 savesel.cpMax -= trim;
\r
7034 if (exlen < 0) exlen = 0;
\r
7035 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7036 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7038 /* Append the new text */
\r
7039 sel.cpMin = exlen;
\r
7040 sel.cpMax = exlen;
\r
7041 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7042 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7043 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7044 if (forceVisible || exlen == 0 ||
\r
7045 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7046 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7047 /* Scroll to make new end of text visible if old end of text
\r
7048 was visible or new text is an echo of user typein */
\r
7049 sel.cpMin = 9999999;
\r
7050 sel.cpMax = 9999999;
\r
7051 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7052 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7053 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7054 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7056 if (savesel.cpMax == exlen || forceVisible) {
\r
7057 /* Move insert point to new end of text if it was at the old
\r
7058 end of text or if the new text is an echo of user typein */
\r
7059 sel.cpMin = 9999999;
\r
7060 sel.cpMax = 9999999;
\r
7061 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7063 /* Restore previous selection */
\r
7064 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7066 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7073 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7077 COLORREF oldFg, oldBg;
\r
7081 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7083 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7084 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7085 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7088 rect.right = x + squareSize;
\r
7090 rect.bottom = y + squareSize;
\r
7093 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7094 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7095 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7096 &rect, str, strlen(str), NULL);
\r
7098 (void) SetTextColor(hdc, oldFg);
\r
7099 (void) SetBkColor(hdc, oldBg);
\r
7100 (void) SelectObject(hdc, oldFont);
\r
7104 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7105 RECT *rect, char *color, char *flagFell)
\r
7109 COLORREF oldFg, oldBg;
\r
7112 if (appData.clockMode) {
\r
7114 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7116 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7123 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7124 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7126 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7127 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7129 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7133 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7134 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7135 rect, str, strlen(str), NULL);
\r
7136 if(logoHeight > 0 && appData.clockMode) {
\r
7138 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7139 r.top = rect->top + logoHeight/2;
\r
7140 r.left = rect->left;
\r
7141 r.right = rect->right;
\r
7142 r.bottom = rect->bottom;
\r
7143 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7144 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7145 &r, str, strlen(str), NULL);
\r
7147 (void) SetTextColor(hdc, oldFg);
\r
7148 (void) SetBkColor(hdc, oldBg);
\r
7149 (void) SelectObject(hdc, oldFont);
\r
7154 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7160 if( count <= 0 ) {
\r
7161 if (appData.debugMode) {
\r
7162 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7165 return ERROR_INVALID_USER_BUFFER;
\r
7168 ResetEvent(ovl->hEvent);
\r
7169 ovl->Offset = ovl->OffsetHigh = 0;
\r
7170 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7174 err = GetLastError();
\r
7175 if (err == ERROR_IO_PENDING) {
\r
7176 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7180 err = GetLastError();
\r
7187 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7192 ResetEvent(ovl->hEvent);
\r
7193 ovl->Offset = ovl->OffsetHigh = 0;
\r
7194 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7198 err = GetLastError();
\r
7199 if (err == ERROR_IO_PENDING) {
\r
7200 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7204 err = GetLastError();
\r
7210 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7211 void CheckForInputBufferFull( InputSource * is )
\r
7213 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7214 /* Look for end of line */
\r
7215 char * p = is->buf;
\r
7217 while( p < is->next && *p != '\n' ) {
\r
7221 if( p >= is->next ) {
\r
7222 if (appData.debugMode) {
\r
7223 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7226 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7227 is->count = (DWORD) -1;
\r
7228 is->next = is->buf;
\r
7234 InputThread(LPVOID arg)
\r
7239 is = (InputSource *) arg;
\r
7240 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7241 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7242 while (is->hThread != NULL) {
\r
7243 is->error = DoReadFile(is->hFile, is->next,
\r
7244 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7245 &is->count, &ovl);
\r
7246 if (is->error == NO_ERROR) {
\r
7247 is->next += is->count;
\r
7249 if (is->error == ERROR_BROKEN_PIPE) {
\r
7250 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7253 is->count = (DWORD) -1;
\r
7254 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7259 CheckForInputBufferFull( is );
\r
7261 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7263 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7265 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7268 CloseHandle(ovl.hEvent);
\r
7269 CloseHandle(is->hFile);
\r
7271 if (appData.debugMode) {
\r
7272 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7279 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7281 NonOvlInputThread(LPVOID arg)
\r
7288 is = (InputSource *) arg;
\r
7289 while (is->hThread != NULL) {
\r
7290 is->error = ReadFile(is->hFile, is->next,
\r
7291 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7292 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7293 if (is->error == NO_ERROR) {
\r
7294 /* Change CRLF to LF */
\r
7295 if (is->next > is->buf) {
\r
7297 i = is->count + 1;
\r
7305 if (prev == '\r' && *p == '\n') {
\r
7317 if (is->error == ERROR_BROKEN_PIPE) {
\r
7318 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7321 is->count = (DWORD) -1;
\r
7325 CheckForInputBufferFull( is );
\r
7327 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7329 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7331 if (is->count < 0) break; /* Quit on error */
\r
7333 CloseHandle(is->hFile);
\r
7338 SocketInputThread(LPVOID arg)
\r
7342 is = (InputSource *) arg;
\r
7343 while (is->hThread != NULL) {
\r
7344 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7345 if ((int)is->count == SOCKET_ERROR) {
\r
7346 is->count = (DWORD) -1;
\r
7347 is->error = WSAGetLastError();
\r
7349 is->error = NO_ERROR;
\r
7350 is->next += is->count;
\r
7351 if (is->count == 0 && is->second == is) {
\r
7352 /* End of file on stderr; quit with no message */
\r
7356 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7358 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7360 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7366 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7370 is = (InputSource *) lParam;
\r
7371 if (is->lineByLine) {
\r
7372 /* Feed in lines one by one */
\r
7373 char *p = is->buf;
\r
7375 while (q < is->next) {
\r
7376 if (*q++ == '\n') {
\r
7377 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7382 /* Move any partial line to the start of the buffer */
\r
7384 while (p < is->next) {
\r
7389 if (is->error != NO_ERROR || is->count == 0) {
\r
7390 /* Notify backend of the error. Note: If there was a partial
\r
7391 line at the end, it is not flushed through. */
\r
7392 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7395 /* Feed in the whole chunk of input at once */
\r
7396 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7397 is->next = is->buf;
\r
7401 /*---------------------------------------------------------------------------*\
\r
7403 * Menu enables. Used when setting various modes.
\r
7405 \*---------------------------------------------------------------------------*/
\r
7413 GreyRevert(Boolean grey)
\r
7414 { // [HGM] vari: for retracting variations in local mode
\r
7415 HMENU hmenu = GetMenu(hwndMain);
\r
7416 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7417 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7421 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7423 while (enab->item > 0) {
\r
7424 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7429 Enables gnuEnables[] = {
\r
7430 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7437 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7439 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7440 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7441 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7442 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7446 Enables icsEnables[] = {
\r
7447 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7448 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7449 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7450 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7451 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7452 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7453 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7454 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7455 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7456 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7457 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7458 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7459 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7460 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7461 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7467 Enables zippyEnables[] = {
\r
7468 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7469 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7470 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7471 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7476 Enables ncpEnables[] = {
\r
7477 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7479 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7480 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7481 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7482 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7483 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7484 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7485 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7486 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7487 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7488 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7489 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7490 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7491 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7492 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7493 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7494 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7495 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7496 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7500 Enables trainingOnEnables[] = {
\r
7501 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7502 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7503 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7504 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7505 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7506 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7507 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7508 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7512 Enables trainingOffEnables[] = {
\r
7513 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7514 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7515 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7516 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7517 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7518 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7519 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7520 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7524 /* These modify either ncpEnables or gnuEnables */
\r
7525 Enables cmailEnables[] = {
\r
7526 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7527 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7528 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7529 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7530 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7531 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7532 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7536 Enables machineThinkingEnables[] = {
\r
7537 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7538 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7539 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7540 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7541 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7542 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7543 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7544 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7545 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7546 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7547 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7548 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7549 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7550 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7551 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7555 Enables userThinkingEnables[] = {
\r
7556 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7557 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7558 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7559 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7560 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7561 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7562 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7563 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7564 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7565 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7566 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7567 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7568 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7569 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7570 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7574 /*---------------------------------------------------------------------------*\
\r
7576 * Front-end interface functions exported by XBoard.
\r
7577 * Functions appear in same order as prototypes in frontend.h.
\r
7579 \*---------------------------------------------------------------------------*/
\r
7583 static UINT prevChecked = 0;
\r
7584 static int prevPausing = 0;
\r
7587 if (pausing != prevPausing) {
\r
7588 prevPausing = pausing;
\r
7589 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7590 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7591 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7594 switch (gameMode) {
\r
7595 case BeginningOfGame:
\r
7596 if (appData.icsActive)
\r
7597 nowChecked = IDM_IcsClient;
\r
7598 else if (appData.noChessProgram)
\r
7599 nowChecked = IDM_EditGame;
\r
7601 nowChecked = IDM_MachineBlack;
\r
7603 case MachinePlaysBlack:
\r
7604 nowChecked = IDM_MachineBlack;
\r
7606 case MachinePlaysWhite:
\r
7607 nowChecked = IDM_MachineWhite;
\r
7609 case TwoMachinesPlay:
\r
7610 nowChecked = IDM_TwoMachines;
\r
7613 nowChecked = IDM_AnalysisMode;
\r
7616 nowChecked = IDM_AnalyzeFile;
\r
7619 nowChecked = IDM_EditGame;
\r
7621 case PlayFromGameFile:
\r
7622 nowChecked = IDM_LoadGame;
\r
7624 case EditPosition:
\r
7625 nowChecked = IDM_EditPosition;
\r
7628 nowChecked = IDM_Training;
\r
7630 case IcsPlayingWhite:
\r
7631 case IcsPlayingBlack:
\r
7632 case IcsObserving:
\r
7634 nowChecked = IDM_IcsClient;
\r
7641 if (prevChecked != 0)
\r
7642 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7643 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7644 if (nowChecked != 0)
\r
7645 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7646 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7648 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7649 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7650 MF_BYCOMMAND|MF_ENABLED);
\r
7652 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7653 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7656 prevChecked = nowChecked;
\r
7658 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7659 if (appData.icsActive) {
\r
7660 if (appData.icsEngineAnalyze) {
\r
7661 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7662 MF_BYCOMMAND|MF_CHECKED);
\r
7664 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7665 MF_BYCOMMAND|MF_UNCHECKED);
\r
7673 HMENU hmenu = GetMenu(hwndMain);
\r
7674 SetMenuEnables(hmenu, icsEnables);
\r
7675 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7676 MF_BYPOSITION|MF_ENABLED);
\r
7678 if (appData.zippyPlay) {
\r
7679 SetMenuEnables(hmenu, zippyEnables);
\r
7680 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7681 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7682 MF_BYCOMMAND|MF_ENABLED);
\r
7690 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7696 HMENU hmenu = GetMenu(hwndMain);
\r
7697 SetMenuEnables(hmenu, ncpEnables);
\r
7698 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7699 MF_BYPOSITION|MF_GRAYED);
\r
7700 DrawMenuBar(hwndMain);
\r
7706 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7710 SetTrainingModeOn()
\r
7713 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7714 for (i = 0; i < N_BUTTONS; i++) {
\r
7715 if (buttonDesc[i].hwnd != NULL)
\r
7716 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7721 VOID SetTrainingModeOff()
\r
7724 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7725 for (i = 0; i < N_BUTTONS; i++) {
\r
7726 if (buttonDesc[i].hwnd != NULL)
\r
7727 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7733 SetUserThinkingEnables()
\r
7735 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7739 SetMachineThinkingEnables()
\r
7741 HMENU hMenu = GetMenu(hwndMain);
\r
7742 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7744 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7746 if (gameMode == MachinePlaysBlack) {
\r
7747 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7748 } else if (gameMode == MachinePlaysWhite) {
\r
7749 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7750 } else if (gameMode == TwoMachinesPlay) {
\r
7751 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7757 DisplayTitle(char *str)
\r
7759 char title[MSG_SIZ], *host;
\r
7760 if (str[0] != NULLCHAR) {
\r
7761 strcpy(title, str);
\r
7762 } else if (appData.icsActive) {
\r
7763 if (appData.icsCommPort[0] != NULLCHAR)
\r
7766 host = appData.icsHost;
\r
7767 sprintf(title, "%s: %s", szTitle, host);
\r
7768 } else if (appData.noChessProgram) {
\r
7769 strcpy(title, szTitle);
\r
7771 strcpy(title, szTitle);
\r
7772 strcat(title, ": ");
\r
7773 strcat(title, first.tidy);
\r
7775 SetWindowText(hwndMain, title);
\r
7780 DisplayMessage(char *str1, char *str2)
\r
7784 int remain = MESSAGE_TEXT_MAX - 1;
\r
7787 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7788 messageText[0] = NULLCHAR;
\r
7790 len = strlen(str1);
\r
7791 if (len > remain) len = remain;
\r
7792 strncpy(messageText, str1, len);
\r
7793 messageText[len] = NULLCHAR;
\r
7796 if (*str2 && remain >= 2) {
\r
7798 strcat(messageText, " ");
\r
7801 len = strlen(str2);
\r
7802 if (len > remain) len = remain;
\r
7803 strncat(messageText, str2, len);
\r
7805 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7807 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7811 hdc = GetDC(hwndMain);
\r
7812 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7813 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7814 &messageRect, messageText, strlen(messageText), NULL);
\r
7815 (void) SelectObject(hdc, oldFont);
\r
7816 (void) ReleaseDC(hwndMain, hdc);
\r
7820 DisplayError(char *str, int error)
\r
7822 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7828 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7829 NULL, error, LANG_NEUTRAL,
\r
7830 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7832 sprintf(buf, "%s:\n%s", str, buf2);
\r
7834 ErrorMap *em = errmap;
\r
7835 while (em->err != 0 && em->err != error) em++;
\r
7836 if (em->err != 0) {
\r
7837 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7839 sprintf(buf, "%s:\nError code %d", str, error);
\r
7844 ErrorPopUp("Error", buf);
\r
7849 DisplayMoveError(char *str)
\r
7851 fromX = fromY = -1;
\r
7852 ClearHighlights();
\r
7853 DrawPosition(FALSE, NULL);
\r
7854 if (appData.popupMoveErrors) {
\r
7855 ErrorPopUp("Error", str);
\r
7857 DisplayMessage(str, "");
\r
7858 moveErrorMessageUp = TRUE;
\r
7863 DisplayFatalError(char *str, int error, int exitStatus)
\r
7865 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7867 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7870 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7871 NULL, error, LANG_NEUTRAL,
\r
7872 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7874 sprintf(buf, "%s:\n%s", str, buf2);
\r
7876 ErrorMap *em = errmap;
\r
7877 while (em->err != 0 && em->err != error) em++;
\r
7878 if (em->err != 0) {
\r
7879 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7881 sprintf(buf, "%s:\nError code %d", str, error);
\r
7886 if (appData.debugMode) {
\r
7887 fprintf(debugFP, "%s: %s\n", label, str);
\r
7889 if (appData.popupExitMessage) {
\r
7890 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7891 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7893 ExitEvent(exitStatus);
\r
7898 DisplayInformation(char *str)
\r
7900 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7905 DisplayNote(char *str)
\r
7907 ErrorPopUp("Note", str);
\r
7912 char *title, *question, *replyPrefix;
\r
7917 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7919 static QuestionParams *qp;
\r
7920 char reply[MSG_SIZ];
\r
7923 switch (message) {
\r
7924 case WM_INITDIALOG:
\r
7925 qp = (QuestionParams *) lParam;
\r
7926 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7927 SetWindowText(hDlg, qp->title);
\r
7928 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7929 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7933 switch (LOWORD(wParam)) {
\r
7935 strcpy(reply, qp->replyPrefix);
\r
7936 if (*reply) strcat(reply, " ");
\r
7937 len = strlen(reply);
\r
7938 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7939 strcat(reply, "\n");
\r
7940 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7941 EndDialog(hDlg, TRUE);
\r
7942 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7945 EndDialog(hDlg, FALSE);
\r
7956 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7958 QuestionParams qp;
\r
7962 qp.question = question;
\r
7963 qp.replyPrefix = replyPrefix;
\r
7965 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7966 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7967 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7968 FreeProcInstance(lpProc);
\r
7971 /* [AS] Pick FRC position */
\r
7972 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7974 static int * lpIndexFRC;
\r
7980 case WM_INITDIALOG:
\r
7981 lpIndexFRC = (int *) lParam;
\r
7983 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7985 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7986 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7987 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7988 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7993 switch( LOWORD(wParam) ) {
\r
7995 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7996 EndDialog( hDlg, 0 );
\r
7997 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8000 EndDialog( hDlg, 1 );
\r
8002 case IDC_NFG_Edit:
\r
8003 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8004 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8006 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8009 case IDC_NFG_Random:
\r
8010 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8011 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8024 int index = appData.defaultFrcPosition;
\r
8025 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8027 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8029 if( result == 0 ) {
\r
8030 appData.defaultFrcPosition = index;
\r
8036 /* [AS] Game list options. Refactored by HGM */
\r
8038 HWND gameListOptionsDialog;
\r
8040 // low-level front-end: clear text edit / list widget
\r
8044 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8047 // low-level front-end: clear text edit / list widget
\r
8049 GLT_DeSelectList()
\r
8051 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8054 // low-level front-end: append line to text edit / list widget
\r
8056 GLT_AddToList( char *name )
\r
8059 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8063 // low-level front-end: get line from text edit / list widget
\r
8065 GLT_GetFromList( int index, char *name )
\r
8068 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8074 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8076 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8077 int idx2 = idx1 + delta;
\r
8078 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8080 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8083 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8084 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8085 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8086 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8090 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8094 case WM_INITDIALOG:
\r
8095 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8097 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8099 /* Initialize list */
\r
8100 GLT_TagsToList( lpUserGLT );
\r
8102 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8107 switch( LOWORD(wParam) ) {
\r
8110 EndDialog( hDlg, 0 );
\r
8113 EndDialog( hDlg, 1 );
\r
8116 case IDC_GLT_Default:
\r
8117 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8120 case IDC_GLT_Restore:
\r
8121 GLT_TagsToList( appData.gameListTags );
\r
8125 GLT_MoveSelection( hDlg, -1 );
\r
8128 case IDC_GLT_Down:
\r
8129 GLT_MoveSelection( hDlg, +1 );
\r
8139 int GameListOptions()
\r
8142 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8144 strcpy( lpUserGLT, appData.gameListTags );
\r
8146 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8148 if( result == 0 ) {
\r
8149 /* [AS] Memory leak here! */
\r
8150 appData.gameListTags = strdup( lpUserGLT );
\r
8157 DisplayIcsInteractionTitle(char *str)
\r
8159 char consoleTitle[MSG_SIZ];
\r
8161 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8162 SetWindowText(hwndConsole, consoleTitle);
\r
8166 DrawPosition(int fullRedraw, Board board)
\r
8168 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8171 void NotifyFrontendLogin()
\r
8174 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8180 fromX = fromY = -1;
\r
8181 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8182 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8183 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8184 dragInfo.lastpos = dragInfo.pos;
\r
8185 dragInfo.start.x = dragInfo.start.y = -1;
\r
8186 dragInfo.from = dragInfo.start;
\r
8188 DrawPosition(TRUE, NULL);
\r
8195 CommentPopUp(char *title, char *str)
\r
8197 HWND hwnd = GetActiveWindow();
\r
8198 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8200 SetActiveWindow(hwnd);
\r
8204 CommentPopDown(void)
\r
8206 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8207 if (commentDialog) {
\r
8208 ShowWindow(commentDialog, SW_HIDE);
\r
8210 commentUp = FALSE;
\r
8214 EditCommentPopUp(int index, char *title, char *str)
\r
8216 EitherCommentPopUp(index, title, str, TRUE);
\r
8223 MyPlaySound(&sounds[(int)SoundMove]);
\r
8226 VOID PlayIcsWinSound()
\r
8228 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8231 VOID PlayIcsLossSound()
\r
8233 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8236 VOID PlayIcsDrawSound()
\r
8238 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8241 VOID PlayIcsUnfinishedSound()
\r
8243 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8249 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8257 consoleEcho = TRUE;
\r
8258 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8259 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8260 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8269 consoleEcho = FALSE;
\r
8270 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8271 /* This works OK: set text and background both to the same color */
\r
8273 cf.crTextColor = COLOR_ECHOOFF;
\r
8274 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8275 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8278 /* No Raw()...? */
\r
8280 void Colorize(ColorClass cc, int continuation)
\r
8282 currentColorClass = cc;
\r
8283 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8284 consoleCF.crTextColor = textAttribs[cc].color;
\r
8285 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8286 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8292 static char buf[MSG_SIZ];
\r
8293 DWORD bufsiz = MSG_SIZ;
\r
8295 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8296 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8298 if (!GetUserName(buf, &bufsiz)) {
\r
8299 /*DisplayError("Error getting user name", GetLastError());*/
\r
8300 strcpy(buf, "User");
\r
8308 static char buf[MSG_SIZ];
\r
8309 DWORD bufsiz = MSG_SIZ;
\r
8311 if (!GetComputerName(buf, &bufsiz)) {
\r
8312 /*DisplayError("Error getting host name", GetLastError());*/
\r
8313 strcpy(buf, "Unknown");
\r
8320 ClockTimerRunning()
\r
8322 return clockTimerEvent != 0;
\r
8328 if (clockTimerEvent == 0) return FALSE;
\r
8329 KillTimer(hwndMain, clockTimerEvent);
\r
8330 clockTimerEvent = 0;
\r
8335 StartClockTimer(long millisec)
\r
8337 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8338 (UINT) millisec, NULL);
\r
8342 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8345 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8347 if(appData.noGUI) return;
\r
8348 hdc = GetDC(hwndMain);
\r
8349 if (!IsIconic(hwndMain)) {
\r
8350 DisplayAClock(hdc, timeRemaining, highlight,
\r
8351 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8353 if (highlight && iconCurrent == iconBlack) {
\r
8354 iconCurrent = iconWhite;
\r
8355 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8356 if (IsIconic(hwndMain)) {
\r
8357 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8360 (void) ReleaseDC(hwndMain, hdc);
\r
8362 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8366 DisplayBlackClock(long timeRemaining, int highlight)
\r
8369 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8371 if(appData.noGUI) return;
\r
8372 hdc = GetDC(hwndMain);
\r
8373 if (!IsIconic(hwndMain)) {
\r
8374 DisplayAClock(hdc, timeRemaining, highlight,
\r
8375 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8377 if (highlight && iconCurrent == iconWhite) {
\r
8378 iconCurrent = iconBlack;
\r
8379 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8380 if (IsIconic(hwndMain)) {
\r
8381 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8384 (void) ReleaseDC(hwndMain, hdc);
\r
8386 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8391 LoadGameTimerRunning()
\r
8393 return loadGameTimerEvent != 0;
\r
8397 StopLoadGameTimer()
\r
8399 if (loadGameTimerEvent == 0) return FALSE;
\r
8400 KillTimer(hwndMain, loadGameTimerEvent);
\r
8401 loadGameTimerEvent = 0;
\r
8406 StartLoadGameTimer(long millisec)
\r
8408 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8409 (UINT) millisec, NULL);
\r
8417 char fileTitle[MSG_SIZ];
\r
8419 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8420 f = OpenFileDialog(hwndMain, "a", defName,
\r
8421 appData.oldSaveStyle ? "gam" : "pgn",
\r
8423 "Save Game to File", NULL, fileTitle, NULL);
\r
8425 SaveGame(f, 0, "");
\r
8432 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8434 if (delayedTimerEvent != 0) {
\r
8435 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8436 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8438 KillTimer(hwndMain, delayedTimerEvent);
\r
8439 delayedTimerEvent = 0;
\r
8440 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8441 delayedTimerCallback();
\r
8443 delayedTimerCallback = cb;
\r
8444 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8445 (UINT) millisec, NULL);
\r
8448 DelayedEventCallback
\r
8451 if (delayedTimerEvent) {
\r
8452 return delayedTimerCallback;
\r
8459 CancelDelayedEvent()
\r
8461 if (delayedTimerEvent) {
\r
8462 KillTimer(hwndMain, delayedTimerEvent);
\r
8463 delayedTimerEvent = 0;
\r
8467 DWORD GetWin32Priority(int nice)
\r
8468 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8470 REALTIME_PRIORITY_CLASS 0x00000100
\r
8471 HIGH_PRIORITY_CLASS 0x00000080
\r
8472 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8473 NORMAL_PRIORITY_CLASS 0x00000020
\r
8474 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8475 IDLE_PRIORITY_CLASS 0x00000040
\r
8477 if (nice < -15) return 0x00000080;
\r
8478 if (nice < 0) return 0x00008000;
\r
8479 if (nice == 0) return 0x00000020;
\r
8480 if (nice < 15) return 0x00004000;
\r
8481 return 0x00000040;
\r
8484 /* Start a child process running the given program.
\r
8485 The process's standard output can be read from "from", and its
\r
8486 standard input can be written to "to".
\r
8487 Exit with fatal error if anything goes wrong.
\r
8488 Returns an opaque pointer that can be used to destroy the process
\r
8492 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8494 #define BUFSIZE 4096
\r
8496 HANDLE hChildStdinRd, hChildStdinWr,
\r
8497 hChildStdoutRd, hChildStdoutWr;
\r
8498 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8499 SECURITY_ATTRIBUTES saAttr;
\r
8501 PROCESS_INFORMATION piProcInfo;
\r
8502 STARTUPINFO siStartInfo;
\r
8504 char buf[MSG_SIZ];
\r
8507 if (appData.debugMode) {
\r
8508 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8513 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8514 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8515 saAttr.bInheritHandle = TRUE;
\r
8516 saAttr.lpSecurityDescriptor = NULL;
\r
8519 * The steps for redirecting child's STDOUT:
\r
8520 * 1. Create anonymous pipe to be STDOUT for child.
\r
8521 * 2. Create a noninheritable duplicate of read handle,
\r
8522 * and close the inheritable read handle.
\r
8525 /* Create a pipe for the child's STDOUT. */
\r
8526 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8527 return GetLastError();
\r
8530 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8531 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8532 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8533 FALSE, /* not inherited */
\r
8534 DUPLICATE_SAME_ACCESS);
\r
8536 return GetLastError();
\r
8538 CloseHandle(hChildStdoutRd);
\r
8541 * The steps for redirecting child's STDIN:
\r
8542 * 1. Create anonymous pipe to be STDIN for child.
\r
8543 * 2. Create a noninheritable duplicate of write handle,
\r
8544 * and close the inheritable write handle.
\r
8547 /* Create a pipe for the child's STDIN. */
\r
8548 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8549 return GetLastError();
\r
8552 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8553 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8554 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8555 FALSE, /* not inherited */
\r
8556 DUPLICATE_SAME_ACCESS);
\r
8558 return GetLastError();
\r
8560 CloseHandle(hChildStdinWr);
\r
8562 /* Arrange to (1) look in dir for the child .exe file, and
\r
8563 * (2) have dir be the child's working directory. Interpret
\r
8564 * dir relative to the directory WinBoard loaded from. */
\r
8565 GetCurrentDirectory(MSG_SIZ, buf);
\r
8566 SetCurrentDirectory(installDir);
\r
8567 SetCurrentDirectory(dir);
\r
8569 /* Now create the child process. */
\r
8571 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8572 siStartInfo.lpReserved = NULL;
\r
8573 siStartInfo.lpDesktop = NULL;
\r
8574 siStartInfo.lpTitle = NULL;
\r
8575 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8576 siStartInfo.cbReserved2 = 0;
\r
8577 siStartInfo.lpReserved2 = NULL;
\r
8578 siStartInfo.hStdInput = hChildStdinRd;
\r
8579 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8580 siStartInfo.hStdError = hChildStdoutWr;
\r
8582 fSuccess = CreateProcess(NULL,
\r
8583 cmdLine, /* command line */
\r
8584 NULL, /* process security attributes */
\r
8585 NULL, /* primary thread security attrs */
\r
8586 TRUE, /* handles are inherited */
\r
8587 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8588 NULL, /* use parent's environment */
\r
8590 &siStartInfo, /* STARTUPINFO pointer */
\r
8591 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8593 err = GetLastError();
\r
8594 SetCurrentDirectory(buf); /* return to prev directory */
\r
8599 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8600 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8601 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8604 /* Close the handles we don't need in the parent */
\r
8605 CloseHandle(piProcInfo.hThread);
\r
8606 CloseHandle(hChildStdinRd);
\r
8607 CloseHandle(hChildStdoutWr);
\r
8609 /* Prepare return value */
\r
8610 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8611 cp->kind = CPReal;
\r
8612 cp->hProcess = piProcInfo.hProcess;
\r
8613 cp->pid = piProcInfo.dwProcessId;
\r
8614 cp->hFrom = hChildStdoutRdDup;
\r
8615 cp->hTo = hChildStdinWrDup;
\r
8617 *pr = (void *) cp;
\r
8619 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8620 2000 where engines sometimes don't see the initial command(s)
\r
8621 from WinBoard and hang. I don't understand how that can happen,
\r
8622 but the Sleep is harmless, so I've put it in. Others have also
\r
8623 reported what may be the same problem, so hopefully this will fix
\r
8624 it for them too. */
\r
8632 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8634 ChildProc *cp; int result;
\r
8636 cp = (ChildProc *) pr;
\r
8637 if (cp == NULL) return;
\r
8639 switch (cp->kind) {
\r
8641 /* TerminateProcess is considered harmful, so... */
\r
8642 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8643 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8644 /* The following doesn't work because the chess program
\r
8645 doesn't "have the same console" as WinBoard. Maybe
\r
8646 we could arrange for this even though neither WinBoard
\r
8647 nor the chess program uses a console for stdio? */
\r
8648 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8650 /* [AS] Special termination modes for misbehaving programs... */
\r
8651 if( signal == 9 ) {
\r
8652 result = TerminateProcess( cp->hProcess, 0 );
\r
8654 if ( appData.debugMode) {
\r
8655 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8658 else if( signal == 10 ) {
\r
8659 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8661 if( dw != WAIT_OBJECT_0 ) {
\r
8662 result = TerminateProcess( cp->hProcess, 0 );
\r
8664 if ( appData.debugMode) {
\r
8665 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8671 CloseHandle(cp->hProcess);
\r
8675 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8679 closesocket(cp->sock);
\r
8684 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8685 closesocket(cp->sock);
\r
8686 closesocket(cp->sock2);
\r
8694 InterruptChildProcess(ProcRef pr)
\r
8698 cp = (ChildProc *) pr;
\r
8699 if (cp == NULL) return;
\r
8700 switch (cp->kind) {
\r
8702 /* The following doesn't work because the chess program
\r
8703 doesn't "have the same console" as WinBoard. Maybe
\r
8704 we could arrange for this even though neither WinBoard
\r
8705 nor the chess program uses a console for stdio */
\r
8706 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8711 /* Can't interrupt */
\r
8715 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8722 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8724 char cmdLine[MSG_SIZ];
\r
8726 if (port[0] == NULLCHAR) {
\r
8727 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8729 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8731 return StartChildProcess(cmdLine, "", pr);
\r
8735 /* Code to open TCP sockets */
\r
8738 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8743 struct sockaddr_in sa, mysa;
\r
8744 struct hostent FAR *hp;
\r
8745 unsigned short uport;
\r
8746 WORD wVersionRequested;
\r
8749 /* Initialize socket DLL */
\r
8750 wVersionRequested = MAKEWORD(1, 1);
\r
8751 err = WSAStartup(wVersionRequested, &wsaData);
\r
8752 if (err != 0) return err;
\r
8755 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8756 err = WSAGetLastError();
\r
8761 /* Bind local address using (mostly) don't-care values.
\r
8763 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8764 mysa.sin_family = AF_INET;
\r
8765 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8766 uport = (unsigned short) 0;
\r
8767 mysa.sin_port = htons(uport);
\r
8768 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8769 == SOCKET_ERROR) {
\r
8770 err = WSAGetLastError();
\r
8775 /* Resolve remote host name */
\r
8776 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8777 if (!(hp = gethostbyname(host))) {
\r
8778 unsigned int b0, b1, b2, b3;
\r
8780 err = WSAGetLastError();
\r
8782 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8783 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8784 hp->h_addrtype = AF_INET;
\r
8786 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8787 hp->h_addr_list[0] = (char *) malloc(4);
\r
8788 hp->h_addr_list[0][0] = (char) b0;
\r
8789 hp->h_addr_list[0][1] = (char) b1;
\r
8790 hp->h_addr_list[0][2] = (char) b2;
\r
8791 hp->h_addr_list[0][3] = (char) b3;
\r
8797 sa.sin_family = hp->h_addrtype;
\r
8798 uport = (unsigned short) atoi(port);
\r
8799 sa.sin_port = htons(uport);
\r
8800 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8802 /* Make connection */
\r
8803 if (connect(s, (struct sockaddr *) &sa,
\r
8804 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8805 err = WSAGetLastError();
\r
8810 /* Prepare return value */
\r
8811 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8812 cp->kind = CPSock;
\r
8814 *pr = (ProcRef *) cp;
\r
8820 OpenCommPort(char *name, ProcRef *pr)
\r
8825 char fullname[MSG_SIZ];
\r
8827 if (*name != '\\')
\r
8828 sprintf(fullname, "\\\\.\\%s", name);
\r
8830 strcpy(fullname, name);
\r
8832 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8833 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8834 if (h == (HANDLE) -1) {
\r
8835 return GetLastError();
\r
8839 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8841 /* Accumulate characters until a 100ms pause, then parse */
\r
8842 ct.ReadIntervalTimeout = 100;
\r
8843 ct.ReadTotalTimeoutMultiplier = 0;
\r
8844 ct.ReadTotalTimeoutConstant = 0;
\r
8845 ct.WriteTotalTimeoutMultiplier = 0;
\r
8846 ct.WriteTotalTimeoutConstant = 0;
\r
8847 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8849 /* Prepare return value */
\r
8850 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8851 cp->kind = CPComm;
\r
8854 *pr = (ProcRef *) cp;
\r
8860 OpenLoopback(ProcRef *pr)
\r
8862 DisplayFatalError("Not implemented", 0, 1);
\r
8868 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8873 struct sockaddr_in sa, mysa;
\r
8874 struct hostent FAR *hp;
\r
8875 unsigned short uport;
\r
8876 WORD wVersionRequested;
\r
8879 char stderrPortStr[MSG_SIZ];
\r
8881 /* Initialize socket DLL */
\r
8882 wVersionRequested = MAKEWORD(1, 1);
\r
8883 err = WSAStartup(wVersionRequested, &wsaData);
\r
8884 if (err != 0) return err;
\r
8886 /* Resolve remote host name */
\r
8887 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8888 if (!(hp = gethostbyname(host))) {
\r
8889 unsigned int b0, b1, b2, b3;
\r
8891 err = WSAGetLastError();
\r
8893 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8894 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8895 hp->h_addrtype = AF_INET;
\r
8897 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8898 hp->h_addr_list[0] = (char *) malloc(4);
\r
8899 hp->h_addr_list[0][0] = (char) b0;
\r
8900 hp->h_addr_list[0][1] = (char) b1;
\r
8901 hp->h_addr_list[0][2] = (char) b2;
\r
8902 hp->h_addr_list[0][3] = (char) b3;
\r
8908 sa.sin_family = hp->h_addrtype;
\r
8909 uport = (unsigned short) 514;
\r
8910 sa.sin_port = htons(uport);
\r
8911 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8913 /* Bind local socket to unused "privileged" port address
\r
8915 s = INVALID_SOCKET;
\r
8916 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8917 mysa.sin_family = AF_INET;
\r
8918 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8919 for (fromPort = 1023;; fromPort--) {
\r
8920 if (fromPort < 0) {
\r
8922 return WSAEADDRINUSE;
\r
8924 if (s == INVALID_SOCKET) {
\r
8925 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8926 err = WSAGetLastError();
\r
8931 uport = (unsigned short) fromPort;
\r
8932 mysa.sin_port = htons(uport);
\r
8933 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8934 == SOCKET_ERROR) {
\r
8935 err = WSAGetLastError();
\r
8936 if (err == WSAEADDRINUSE) continue;
\r
8940 if (connect(s, (struct sockaddr *) &sa,
\r
8941 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8942 err = WSAGetLastError();
\r
8943 if (err == WSAEADDRINUSE) {
\r
8954 /* Bind stderr local socket to unused "privileged" port address
\r
8956 s2 = INVALID_SOCKET;
\r
8957 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8958 mysa.sin_family = AF_INET;
\r
8959 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8960 for (fromPort = 1023;; fromPort--) {
\r
8961 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8962 if (fromPort < 0) {
\r
8963 (void) closesocket(s);
\r
8965 return WSAEADDRINUSE;
\r
8967 if (s2 == INVALID_SOCKET) {
\r
8968 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8969 err = WSAGetLastError();
\r
8975 uport = (unsigned short) fromPort;
\r
8976 mysa.sin_port = htons(uport);
\r
8977 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8978 == SOCKET_ERROR) {
\r
8979 err = WSAGetLastError();
\r
8980 if (err == WSAEADDRINUSE) continue;
\r
8981 (void) closesocket(s);
\r
8985 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8986 err = WSAGetLastError();
\r
8987 if (err == WSAEADDRINUSE) {
\r
8989 s2 = INVALID_SOCKET;
\r
8992 (void) closesocket(s);
\r
8993 (void) closesocket(s2);
\r
8999 prevStderrPort = fromPort; // remember port used
\r
9000 sprintf(stderrPortStr, "%d", fromPort);
\r
9002 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9003 err = WSAGetLastError();
\r
9004 (void) closesocket(s);
\r
9005 (void) closesocket(s2);
\r
9010 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9011 err = WSAGetLastError();
\r
9012 (void) closesocket(s);
\r
9013 (void) closesocket(s2);
\r
9017 if (*user == NULLCHAR) user = UserName();
\r
9018 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9019 err = WSAGetLastError();
\r
9020 (void) closesocket(s);
\r
9021 (void) closesocket(s2);
\r
9025 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9026 err = WSAGetLastError();
\r
9027 (void) closesocket(s);
\r
9028 (void) closesocket(s2);
\r
9033 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9034 err = WSAGetLastError();
\r
9035 (void) closesocket(s);
\r
9036 (void) closesocket(s2);
\r
9040 (void) closesocket(s2); /* Stop listening */
\r
9042 /* Prepare return value */
\r
9043 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9044 cp->kind = CPRcmd;
\r
9047 *pr = (ProcRef *) cp;
\r
9054 AddInputSource(ProcRef pr, int lineByLine,
\r
9055 InputCallback func, VOIDSTAR closure)
\r
9057 InputSource *is, *is2 = NULL;
\r
9058 ChildProc *cp = (ChildProc *) pr;
\r
9060 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9061 is->lineByLine = lineByLine;
\r
9063 is->closure = closure;
\r
9064 is->second = NULL;
\r
9065 is->next = is->buf;
\r
9066 if (pr == NoProc) {
\r
9067 is->kind = CPReal;
\r
9068 consoleInputSource = is;
\r
9070 is->kind = cp->kind;
\r
9072 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9073 we create all threads suspended so that the is->hThread variable can be
\r
9074 safely assigned, then let the threads start with ResumeThread.
\r
9076 switch (cp->kind) {
\r
9078 is->hFile = cp->hFrom;
\r
9079 cp->hFrom = NULL; /* now owned by InputThread */
\r
9081 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9082 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9086 is->hFile = cp->hFrom;
\r
9087 cp->hFrom = NULL; /* now owned by InputThread */
\r
9089 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9090 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9094 is->sock = cp->sock;
\r
9096 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9097 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9101 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9103 is->sock = cp->sock;
\r
9105 is2->sock = cp->sock2;
\r
9106 is2->second = is2;
\r
9108 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9109 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9111 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9112 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9116 if( is->hThread != NULL ) {
\r
9117 ResumeThread( is->hThread );
\r
9120 if( is2 != NULL && is2->hThread != NULL ) {
\r
9121 ResumeThread( is2->hThread );
\r
9125 return (InputSourceRef) is;
\r
9129 RemoveInputSource(InputSourceRef isr)
\r
9133 is = (InputSource *) isr;
\r
9134 is->hThread = NULL; /* tell thread to stop */
\r
9135 CloseHandle(is->hThread);
\r
9136 if (is->second != NULL) {
\r
9137 is->second->hThread = NULL;
\r
9138 CloseHandle(is->second->hThread);
\r
9142 int no_wrap(char *message, int count)
\r
9144 ConsoleOutput(message, count, FALSE);
\r
9149 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9152 int outCount = SOCKET_ERROR;
\r
9153 ChildProc *cp = (ChildProc *) pr;
\r
9154 static OVERLAPPED ovl;
\r
9155 static int line = 0;
\r
9159 if (appData.noJoin || !appData.useInternalWrap)
\r
9160 return no_wrap(message, count);
\r
9163 int width = get_term_width();
\r
9164 int len = wrap(NULL, message, count, width, &line);
\r
9165 char *msg = malloc(len);
\r
9169 return no_wrap(message, count);
\r
9172 dbgchk = wrap(msg, message, count, width, &line);
\r
9173 if (dbgchk != len && appData.debugMode)
\r
9174 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9175 ConsoleOutput(msg, len, FALSE);
\r
9182 if (ovl.hEvent == NULL) {
\r
9183 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9185 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9187 switch (cp->kind) {
\r
9190 outCount = send(cp->sock, message, count, 0);
\r
9191 if (outCount == SOCKET_ERROR) {
\r
9192 *outError = WSAGetLastError();
\r
9194 *outError = NO_ERROR;
\r
9199 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9200 &dOutCount, NULL)) {
\r
9201 *outError = NO_ERROR;
\r
9202 outCount = (int) dOutCount;
\r
9204 *outError = GetLastError();
\r
9209 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9210 &dOutCount, &ovl);
\r
9211 if (*outError == NO_ERROR) {
\r
9212 outCount = (int) dOutCount;
\r
9220 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9223 /* Ignore delay, not implemented for WinBoard */
\r
9224 return OutputToProcess(pr, message, count, outError);
\r
9229 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9230 char *buf, int count, int error)
\r
9232 DisplayFatalError("Not implemented", 0, 1);
\r
9235 /* see wgamelist.c for Game List functions */
\r
9236 /* see wedittags.c for Edit Tags functions */
\r
9243 char buf[MSG_SIZ];
\r
9246 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9247 f = fopen(buf, "r");
\r
9249 ProcessICSInitScript(f);
\r
9257 StartAnalysisClock()
\r
9259 if (analysisTimerEvent) return;
\r
9260 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9261 (UINT) 2000, NULL);
\r
9265 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9267 highlightInfo.sq[0].x = fromX;
\r
9268 highlightInfo.sq[0].y = fromY;
\r
9269 highlightInfo.sq[1].x = toX;
\r
9270 highlightInfo.sq[1].y = toY;
\r
9276 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9277 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9281 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9283 premoveHighlightInfo.sq[0].x = fromX;
\r
9284 premoveHighlightInfo.sq[0].y = fromY;
\r
9285 premoveHighlightInfo.sq[1].x = toX;
\r
9286 premoveHighlightInfo.sq[1].y = toY;
\r
9290 ClearPremoveHighlights()
\r
9292 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9293 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9297 ShutDownFrontEnd()
\r
9299 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9300 DeleteClipboardTempFiles();
\r
9306 if (IsIconic(hwndMain))
\r
9307 ShowWindow(hwndMain, SW_RESTORE);
\r
9309 SetActiveWindow(hwndMain);
\r
9313 * Prototypes for animation support routines
\r
9315 static void ScreenSquare(int column, int row, POINT * pt);
\r
9316 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9317 POINT frames[], int * nFrames);
\r
9321 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9322 { // [HGM] atomic: animate blast wave
\r
9324 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9325 explodeInfo.fromX = fromX;
\r
9326 explodeInfo.fromY = fromY;
\r
9327 explodeInfo.toX = toX;
\r
9328 explodeInfo.toY = toY;
\r
9329 for(i=1; i<nFrames; i++) {
\r
9330 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9331 DrawPosition(FALSE, NULL);
\r
9332 Sleep(appData.animSpeed);
\r
9334 explodeInfo.radius = 0;
\r
9335 DrawPosition(TRUE, NULL);
\r
9341 AnimateMove(board, fromX, fromY, toX, toY)
\r
9348 ChessSquare piece;
\r
9349 POINT start, finish, mid;
\r
9350 POINT frames[kFactor * 2 + 1];
\r
9353 if (!appData.animate) return;
\r
9354 if (doingSizing) return;
\r
9355 if (fromY < 0 || fromX < 0) return;
\r
9356 piece = board[fromY][fromX];
\r
9357 if (piece >= EmptySquare) return;
\r
9359 ScreenSquare(fromX, fromY, &start);
\r
9360 ScreenSquare(toX, toY, &finish);
\r
9362 /* All pieces except knights move in straight line */
\r
9363 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9364 mid.x = start.x + (finish.x - start.x) / 2;
\r
9365 mid.y = start.y + (finish.y - start.y) / 2;
\r
9367 /* Knight: make diagonal movement then straight */
\r
9368 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9369 mid.x = start.x + (finish.x - start.x) / 2;
\r
9373 mid.y = start.y + (finish.y - start.y) / 2;
\r
9377 /* Don't use as many frames for very short moves */
\r
9378 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9379 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9381 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9383 animInfo.from.x = fromX;
\r
9384 animInfo.from.y = fromY;
\r
9385 animInfo.to.x = toX;
\r
9386 animInfo.to.y = toY;
\r
9387 animInfo.lastpos = start;
\r
9388 animInfo.piece = piece;
\r
9389 for (n = 0; n < nFrames; n++) {
\r
9390 animInfo.pos = frames[n];
\r
9391 DrawPosition(FALSE, NULL);
\r
9392 animInfo.lastpos = animInfo.pos;
\r
9393 Sleep(appData.animSpeed);
\r
9395 animInfo.pos = finish;
\r
9396 DrawPosition(FALSE, NULL);
\r
9397 animInfo.piece = EmptySquare;
\r
9398 if(gameInfo.variant == VariantAtomic &&
\r
9399 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9400 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9403 /* Convert board position to corner of screen rect and color */
\r
9406 ScreenSquare(column, row, pt)
\r
9407 int column; int row; POINT * pt;
\r
9410 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9411 pt->y = lineGap + row * (squareSize + lineGap);
\r
9413 pt->x = lineGap + column * (squareSize + lineGap);
\r
9414 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9418 /* Generate a series of frame coords from start->mid->finish.
\r
9419 The movement rate doubles until the half way point is
\r
9420 reached, then halves back down to the final destination,
\r
9421 which gives a nice slow in/out effect. The algorithmn
\r
9422 may seem to generate too many intermediates for short
\r
9423 moves, but remember that the purpose is to attract the
\r
9424 viewers attention to the piece about to be moved and
\r
9425 then to where it ends up. Too few frames would be less
\r
9429 Tween(start, mid, finish, factor, frames, nFrames)
\r
9430 POINT * start; POINT * mid;
\r
9431 POINT * finish; int factor;
\r
9432 POINT frames[]; int * nFrames;
\r
9434 int n, fraction = 1, count = 0;
\r
9436 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9437 for (n = 0; n < factor; n++)
\r
9439 for (n = 0; n < factor; n++) {
\r
9440 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9441 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9443 fraction = fraction / 2;
\r
9447 frames[count] = *mid;
\r
9450 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9452 for (n = 0; n < factor; n++) {
\r
9453 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9454 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9456 fraction = fraction * 2;
\r
9462 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9464 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9466 EvalGraphSet( first, last, current, pvInfoList );
\r