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 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2659 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2660 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2665 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2666 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2667 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2668 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2675 /* [AS] Arrow highlighting support */
\r
2677 static int A_WIDTH = 5; /* Width of arrow body */
\r
2679 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2680 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2682 static double Sqr( double x )
\r
2687 static int Round( double x )
\r
2689 return (int) (x + 0.5);
\r
2692 /* Draw an arrow between two points using current settings */
\r
2693 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2696 double dx, dy, j, k, x, y;
\r
2698 if( d_x == s_x ) {
\r
2699 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2701 arrow[0].x = s_x + A_WIDTH;
\r
2704 arrow[1].x = s_x + A_WIDTH;
\r
2705 arrow[1].y = d_y - h;
\r
2707 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2708 arrow[2].y = d_y - h;
\r
2713 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2714 arrow[4].y = d_y - h;
\r
2716 arrow[5].x = s_x - A_WIDTH;
\r
2717 arrow[5].y = d_y - h;
\r
2719 arrow[6].x = s_x - A_WIDTH;
\r
2722 else if( d_y == s_y ) {
\r
2723 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2726 arrow[0].y = s_y + A_WIDTH;
\r
2728 arrow[1].x = d_x - w;
\r
2729 arrow[1].y = s_y + A_WIDTH;
\r
2731 arrow[2].x = d_x - w;
\r
2732 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2737 arrow[4].x = d_x - w;
\r
2738 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2740 arrow[5].x = d_x - w;
\r
2741 arrow[5].y = s_y - A_WIDTH;
\r
2744 arrow[6].y = s_y - A_WIDTH;
\r
2747 /* [AS] Needed a lot of paper for this! :-) */
\r
2748 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2749 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2751 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2753 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2758 arrow[0].x = Round(x - j);
\r
2759 arrow[0].y = Round(y + j*dx);
\r
2761 arrow[1].x = Round(x + j);
\r
2762 arrow[1].y = Round(y - j*dx);
\r
2765 x = (double) d_x - k;
\r
2766 y = (double) d_y - k*dy;
\r
2769 x = (double) d_x + k;
\r
2770 y = (double) d_y + k*dy;
\r
2773 arrow[2].x = Round(x + j);
\r
2774 arrow[2].y = Round(y - j*dx);
\r
2776 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2777 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2782 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2783 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2785 arrow[6].x = Round(x - j);
\r
2786 arrow[6].y = Round(y + j*dx);
\r
2789 Polygon( hdc, arrow, 7 );
\r
2792 /* [AS] Draw an arrow between two squares */
\r
2793 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2795 int s_x, s_y, d_x, d_y;
\r
2802 if( s_col == d_col && s_row == d_row ) {
\r
2806 /* Get source and destination points */
\r
2807 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2808 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2811 d_y += squareSize / 4;
\r
2813 else if( d_y < s_y ) {
\r
2814 d_y += 3 * squareSize / 4;
\r
2817 d_y += squareSize / 2;
\r
2821 d_x += squareSize / 4;
\r
2823 else if( d_x < s_x ) {
\r
2824 d_x += 3 * squareSize / 4;
\r
2827 d_x += squareSize / 2;
\r
2830 s_x += squareSize / 2;
\r
2831 s_y += squareSize / 2;
\r
2833 /* Adjust width */
\r
2834 A_WIDTH = squareSize / 14;
\r
2837 stLB.lbStyle = BS_SOLID;
\r
2838 stLB.lbColor = appData.highlightArrowColor;
\r
2841 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2842 holdpen = SelectObject( hdc, hpen );
\r
2843 hbrush = CreateBrushIndirect( &stLB );
\r
2844 holdbrush = SelectObject( hdc, hbrush );
\r
2846 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2848 SelectObject( hdc, holdpen );
\r
2849 SelectObject( hdc, holdbrush );
\r
2850 DeleteObject( hpen );
\r
2851 DeleteObject( hbrush );
\r
2854 BOOL HasHighlightInfo()
\r
2856 BOOL result = FALSE;
\r
2858 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2859 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2867 BOOL IsDrawArrowEnabled()
\r
2869 BOOL result = FALSE;
\r
2871 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2878 VOID DrawArrowHighlight( HDC hdc )
\r
2880 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2881 DrawArrowBetweenSquares( hdc,
\r
2882 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2883 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2887 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2889 HRGN result = NULL;
\r
2891 if( HasHighlightInfo() ) {
\r
2892 int x1, y1, x2, y2;
\r
2893 int sx, sy, dx, dy;
\r
2895 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2896 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2898 sx = MIN( x1, x2 );
\r
2899 sy = MIN( y1, y2 );
\r
2900 dx = MAX( x1, x2 ) + squareSize;
\r
2901 dy = MAX( y1, y2 ) + squareSize;
\r
2903 result = CreateRectRgn( sx, sy, dx, dy );
\r
2910 Warning: this function modifies the behavior of several other functions.
\r
2912 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2913 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2914 repaint is scattered all over the place, which is not good for features such as
\r
2915 "arrow highlighting" that require a full repaint of the board.
\r
2917 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2918 user interaction, when speed is not so important) but especially to avoid errors
\r
2919 in the displayed graphics.
\r
2921 In such patched places, I always try refer to this function so there is a single
\r
2922 place to maintain knowledge.
\r
2924 To restore the original behavior, just return FALSE unconditionally.
\r
2926 BOOL IsFullRepaintPreferrable()
\r
2928 BOOL result = FALSE;
\r
2930 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2931 /* Arrow may appear on the board */
\r
2939 This function is called by DrawPosition to know whether a full repaint must
\r
2942 Only DrawPosition may directly call this function, which makes use of
\r
2943 some state information. Other function should call DrawPosition specifying
\r
2944 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2946 BOOL DrawPositionNeedsFullRepaint()
\r
2948 BOOL result = FALSE;
\r
2951 Probably a slightly better policy would be to trigger a full repaint
\r
2952 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2953 but animation is fast enough that it's difficult to notice.
\r
2955 if( animInfo.piece == EmptySquare ) {
\r
2956 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2965 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2967 int row, column, x, y, square_color, piece_color;
\r
2968 ChessSquare piece;
\r
2970 HDC texture_hdc = NULL;
\r
2972 /* [AS] Initialize background textures if needed */
\r
2973 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2974 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2975 if( backTextureSquareSize != squareSize
\r
2976 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2977 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2978 backTextureSquareSize = squareSize;
\r
2979 RebuildTextureSquareInfo();
\r
2982 texture_hdc = CreateCompatibleDC( hdc );
\r
2985 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2986 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2988 SquareToPos(row, column, &x, &y);
\r
2990 piece = board[row][column];
\r
2992 square_color = ((column + row) % 2) == 1;
\r
2993 if( gameInfo.variant == VariantXiangqi ) {
\r
2994 square_color = !InPalace(row, column);
\r
2995 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2996 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2998 piece_color = (int) piece < (int) BlackPawn;
\r
3001 /* [HGM] holdings file: light square or black */
\r
3002 if(column == BOARD_LEFT-2) {
\r
3003 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3006 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3010 if(column == BOARD_RGHT + 1 ) {
\r
3011 if( row < gameInfo.holdingsSize )
\r
3014 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3018 if(column == BOARD_LEFT-1 ) /* left align */
\r
3019 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3020 else if( column == BOARD_RGHT) /* right align */
\r
3021 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3023 if (appData.monoMode) {
\r
3024 if (piece == EmptySquare) {
\r
3025 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3026 square_color ? WHITENESS : BLACKNESS);
\r
3028 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3031 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3032 /* [AS] Draw the square using a texture bitmap */
\r
3033 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3034 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3035 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3038 squareSize, squareSize,
\r
3041 backTextureSquareInfo[r][c].mode,
\r
3042 backTextureSquareInfo[r][c].x,
\r
3043 backTextureSquareInfo[r][c].y );
\r
3045 SelectObject( texture_hdc, hbm );
\r
3047 if (piece != EmptySquare) {
\r
3048 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3052 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3054 oldBrush = SelectObject(hdc, brush );
\r
3055 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3056 SelectObject(hdc, oldBrush);
\r
3057 if (piece != EmptySquare)
\r
3058 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3063 if( texture_hdc != NULL ) {
\r
3064 DeleteDC( texture_hdc );
\r
3068 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3069 void fputDW(FILE *f, int x)
\r
3071 fputc(x & 255, f);
\r
3072 fputc(x>>8 & 255, f);
\r
3073 fputc(x>>16 & 255, f);
\r
3074 fputc(x>>24 & 255, f);
\r
3077 #define MAX_CLIPS 200 /* more than enough */
\r
3080 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3082 // HBITMAP bufferBitmap;
\r
3087 int w = 100, h = 50;
\r
3089 if(logo == NULL) return;
\r
3090 // GetClientRect(hwndMain, &Rect);
\r
3091 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3092 // Rect.bottom-Rect.top+1);
\r
3093 tmphdc = CreateCompatibleDC(hdc);
\r
3094 hbm = SelectObject(tmphdc, logo);
\r
3095 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3099 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3100 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3101 SelectObject(tmphdc, hbm);
\r
3105 static HDC hdcSeek;
\r
3107 // [HGM] seekgraph
\r
3108 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3111 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3112 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3113 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3114 SelectObject( hdcSeek, hp );
\r
3117 // front-end wrapper for drawing functions to do rectangles
\r
3118 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3123 if (hdcSeek == NULL) {
\r
3124 hdcSeek = GetDC(hwndMain);
\r
3125 if (!appData.monoMode) {
\r
3126 SelectPalette(hdcSeek, hPal, FALSE);
\r
3127 RealizePalette(hdcSeek);
\r
3130 hp = SelectObject( hdcSeek, gridPen );
\r
3131 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3132 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3133 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3134 SelectObject( hdcSeek, hp );
\r
3137 // front-end wrapper for putting text in graph
\r
3138 void DrawSeekText(char *buf, int x, int y)
\r
3141 SetBkMode( hdcSeek, TRANSPARENT );
\r
3142 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3143 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3146 void DrawSeekDot(int x, int y, int color)
\r
3148 int square = color & 0x80;
\r
3150 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3151 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3153 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3154 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3156 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3157 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3158 SelectObject(hdcSeek, oldBrush);
\r
3162 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3164 static Board lastReq[2], lastDrawn[2];
\r
3165 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3166 static int lastDrawnFlipView = 0;
\r
3167 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3168 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3171 HBITMAP bufferBitmap;
\r
3172 HBITMAP oldBitmap;
\r
3174 HRGN clips[MAX_CLIPS];
\r
3175 ChessSquare dragged_piece = EmptySquare;
\r
3176 int nr = twoBoards*partnerUp;
\r
3178 /* I'm undecided on this - this function figures out whether a full
\r
3179 * repaint is necessary on its own, so there's no real reason to have the
\r
3180 * caller tell it that. I think this can safely be set to FALSE - but
\r
3181 * if we trust the callers not to request full repaints unnessesarily, then
\r
3182 * we could skip some clipping work. In other words, only request a full
\r
3183 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3184 * gamestart and similar) --Hawk
\r
3186 Boolean fullrepaint = repaint;
\r
3188 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3190 if( DrawPositionNeedsFullRepaint() ) {
\r
3191 fullrepaint = TRUE;
\r
3194 if (board == NULL) {
\r
3195 if (!lastReqValid[nr]) {
\r
3198 board = lastReq[nr];
\r
3200 CopyBoard(lastReq[nr], board);
\r
3201 lastReqValid[nr] = 1;
\r
3204 if (doingSizing) {
\r
3208 if (IsIconic(hwndMain)) {
\r
3212 if (hdc == NULL) {
\r
3213 hdc = GetDC(hwndMain);
\r
3214 if (!appData.monoMode) {
\r
3215 SelectPalette(hdc, hPal, FALSE);
\r
3216 RealizePalette(hdc);
\r
3220 releaseDC = FALSE;
\r
3223 /* Create some work-DCs */
\r
3224 hdcmem = CreateCompatibleDC(hdc);
\r
3225 tmphdc = CreateCompatibleDC(hdc);
\r
3227 /* If dragging is in progress, we temporarely remove the piece */
\r
3228 /* [HGM] or temporarily decrease count if stacked */
\r
3229 /* !! Moved to before board compare !! */
\r
3230 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3231 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3232 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3233 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3234 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3236 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3237 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3238 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3240 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3243 /* Figure out which squares need updating by comparing the
\r
3244 * newest board with the last drawn board and checking if
\r
3245 * flipping has changed.
\r
3247 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3248 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3249 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3250 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3251 SquareToPos(row, column, &x, &y);
\r
3252 clips[num_clips++] =
\r
3253 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3257 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3258 for (i=0; i<2; i++) {
\r
3259 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3260 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3261 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3262 lastDrawnHighlight.sq[i].y >= 0) {
\r
3263 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3264 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3265 clips[num_clips++] =
\r
3266 CreateRectRgn(x - lineGap, y - lineGap,
\r
3267 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3269 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3270 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3271 clips[num_clips++] =
\r
3272 CreateRectRgn(x - lineGap, y - lineGap,
\r
3273 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3277 for (i=0; i<2; i++) {
\r
3278 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3279 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3280 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3281 lastDrawnPremove.sq[i].y >= 0) {
\r
3282 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3283 lastDrawnPremove.sq[i].x, &x, &y);
\r
3284 clips[num_clips++] =
\r
3285 CreateRectRgn(x - lineGap, y - lineGap,
\r
3286 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3288 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3289 premoveHighlightInfo.sq[i].y >= 0) {
\r
3290 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3291 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3292 clips[num_clips++] =
\r
3293 CreateRectRgn(x - lineGap, y - lineGap,
\r
3294 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3298 } else { // nr == 1
\r
3299 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3300 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3301 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3302 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3303 for (i=0; i<2; i++) {
\r
3304 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3305 partnerHighlightInfo.sq[i].y >= 0) {
\r
3306 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3307 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3308 clips[num_clips++] =
\r
3309 CreateRectRgn(x - lineGap, y - lineGap,
\r
3310 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3312 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3313 oldPartnerHighlight.sq[i].y >= 0) {
\r
3314 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3315 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3316 clips[num_clips++] =
\r
3317 CreateRectRgn(x - lineGap, y - lineGap,
\r
3318 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3323 fullrepaint = TRUE;
\r
3326 /* Create a buffer bitmap - this is the actual bitmap
\r
3327 * being written to. When all the work is done, we can
\r
3328 * copy it to the real DC (the screen). This avoids
\r
3329 * the problems with flickering.
\r
3331 GetClientRect(hwndMain, &Rect);
\r
3332 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3333 Rect.bottom-Rect.top+1);
\r
3334 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3335 if (!appData.monoMode) {
\r
3336 SelectPalette(hdcmem, hPal, FALSE);
\r
3339 /* Create clips for dragging */
\r
3340 if (!fullrepaint) {
\r
3341 if (dragInfo.from.x >= 0) {
\r
3342 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3343 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3345 if (dragInfo.start.x >= 0) {
\r
3346 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3347 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3349 if (dragInfo.pos.x >= 0) {
\r
3350 x = dragInfo.pos.x - squareSize / 2;
\r
3351 y = dragInfo.pos.y - squareSize / 2;
\r
3352 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3354 if (dragInfo.lastpos.x >= 0) {
\r
3355 x = dragInfo.lastpos.x - squareSize / 2;
\r
3356 y = dragInfo.lastpos.y - squareSize / 2;
\r
3357 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3361 /* Are we animating a move?
\r
3363 * - remove the piece from the board (temporarely)
\r
3364 * - calculate the clipping region
\r
3366 if (!fullrepaint) {
\r
3367 if (animInfo.piece != EmptySquare) {
\r
3368 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3369 x = boardRect.left + animInfo.lastpos.x;
\r
3370 y = boardRect.top + animInfo.lastpos.y;
\r
3371 x2 = boardRect.left + animInfo.pos.x;
\r
3372 y2 = boardRect.top + animInfo.pos.y;
\r
3373 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3374 /* Slight kludge. The real problem is that after AnimateMove is
\r
3375 done, the position on the screen does not match lastDrawn.
\r
3376 This currently causes trouble only on e.p. captures in
\r
3377 atomic, where the piece moves to an empty square and then
\r
3378 explodes. The old and new positions both had an empty square
\r
3379 at the destination, but animation has drawn a piece there and
\r
3380 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3381 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3385 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3386 if (num_clips == 0)
\r
3387 fullrepaint = TRUE;
\r
3389 /* Set clipping on the memory DC */
\r
3390 if (!fullrepaint) {
\r
3391 SelectClipRgn(hdcmem, clips[0]);
\r
3392 for (x = 1; x < num_clips; x++) {
\r
3393 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3394 abort(); // this should never ever happen!
\r
3398 /* Do all the drawing to the memory DC */
\r
3399 if(explodeInfo.radius) { // [HGM] atomic
\r
3401 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3402 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3403 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3404 x += squareSize/2;
\r
3405 y += squareSize/2;
\r
3406 if(!fullrepaint) {
\r
3407 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3408 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3410 DrawGridOnDC(hdcmem);
\r
3411 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3412 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3413 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3414 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3415 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3416 SelectObject(hdcmem, oldBrush);
\r
3418 DrawGridOnDC(hdcmem);
\r
3419 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3420 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3421 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3423 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3424 oldPartnerHighlight = partnerHighlightInfo;
\r
3426 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3428 if(nr == 0) // [HGM] dual: markers only on left board
\r
3429 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3430 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3431 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3432 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3433 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3434 SquareToPos(row, column, &x, &y);
\r
3435 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3436 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3437 SelectObject(hdcmem, oldBrush);
\r
3442 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3443 if(appData.autoLogo) {
\r
3445 switch(gameMode) { // pick logos based on game mode
\r
3446 case IcsObserving:
\r
3447 whiteLogo = second.programLogo; // ICS logo
\r
3448 blackLogo = second.programLogo;
\r
3451 case IcsPlayingWhite:
\r
3452 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3453 blackLogo = second.programLogo; // ICS logo
\r
3455 case IcsPlayingBlack:
\r
3456 whiteLogo = second.programLogo; // ICS logo
\r
3457 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3459 case TwoMachinesPlay:
\r
3460 if(first.twoMachinesColor[0] == 'b') {
\r
3461 whiteLogo = second.programLogo;
\r
3462 blackLogo = first.programLogo;
\r
3465 case MachinePlaysWhite:
\r
3466 blackLogo = userLogo;
\r
3468 case MachinePlaysBlack:
\r
3469 whiteLogo = userLogo;
\r
3470 blackLogo = first.programLogo;
\r
3473 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3474 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3477 if( appData.highlightMoveWithArrow ) {
\r
3478 DrawArrowHighlight(hdcmem);
\r
3481 DrawCoordsOnDC(hdcmem);
\r
3483 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3484 /* to make sure lastDrawn contains what is actually drawn */
\r
3486 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3487 if (dragged_piece != EmptySquare) {
\r
3488 /* [HGM] or restack */
\r
3489 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3490 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3492 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3493 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3494 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3495 x = dragInfo.pos.x - squareSize / 2;
\r
3496 y = dragInfo.pos.y - squareSize / 2;
\r
3497 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3498 ((int) dragged_piece < (int) BlackPawn),
\r
3499 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3502 /* Put the animated piece back into place and draw it */
\r
3503 if (animInfo.piece != EmptySquare) {
\r
3504 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3505 x = boardRect.left + animInfo.pos.x;
\r
3506 y = boardRect.top + animInfo.pos.y;
\r
3507 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3508 ((int) animInfo.piece < (int) BlackPawn),
\r
3509 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3512 /* Release the bufferBitmap by selecting in the old bitmap
\r
3513 * and delete the memory DC
\r
3515 SelectObject(hdcmem, oldBitmap);
\r
3518 /* Set clipping on the target DC */
\r
3519 if (!fullrepaint) {
\r
3520 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3522 GetRgnBox(clips[x], &rect);
\r
3523 DeleteObject(clips[x]);
\r
3524 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3525 rect.right + wpMain.width/2, rect.bottom);
\r
3527 SelectClipRgn(hdc, clips[0]);
\r
3528 for (x = 1; x < num_clips; x++) {
\r
3529 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3530 abort(); // this should never ever happen!
\r
3534 /* Copy the new bitmap onto the screen in one go.
\r
3535 * This way we avoid any flickering
\r
3537 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3538 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3539 boardRect.right - boardRect.left,
\r
3540 boardRect.bottom - boardRect.top,
\r
3541 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3542 if(saveDiagFlag) {
\r
3543 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3544 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3546 GetObject(bufferBitmap, sizeof(b), &b);
\r
3547 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3548 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3549 bih.biWidth = b.bmWidth;
\r
3550 bih.biHeight = b.bmHeight;
\r
3552 bih.biBitCount = b.bmBitsPixel;
\r
3553 bih.biCompression = 0;
\r
3554 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3555 bih.biXPelsPerMeter = 0;
\r
3556 bih.biYPelsPerMeter = 0;
\r
3557 bih.biClrUsed = 0;
\r
3558 bih.biClrImportant = 0;
\r
3559 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3560 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3561 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3562 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3564 wb = b.bmWidthBytes;
\r
3566 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3567 int k = ((int*) pData)[i];
\r
3568 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3569 if(j >= 16) break;
\r
3571 if(j >= nrColors) nrColors = j+1;
\r
3573 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3575 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3576 for(w=0; w<(wb>>2); w+=2) {
\r
3577 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3578 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3579 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3580 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3581 pData[p++] = m | j<<4;
\r
3583 while(p&3) pData[p++] = 0;
\r
3586 wb = ((wb+31)>>5)<<2;
\r
3588 // write BITMAPFILEHEADER
\r
3589 fprintf(diagFile, "BM");
\r
3590 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3591 fputDW(diagFile, 0);
\r
3592 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3593 // write BITMAPINFOHEADER
\r
3594 fputDW(diagFile, 40);
\r
3595 fputDW(diagFile, b.bmWidth);
\r
3596 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3597 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3598 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3599 fputDW(diagFile, 0);
\r
3600 fputDW(diagFile, 0);
\r
3601 fputDW(diagFile, 0);
\r
3602 fputDW(diagFile, 0);
\r
3603 fputDW(diagFile, 0);
\r
3604 fputDW(diagFile, 0);
\r
3605 // write color table
\r
3607 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3608 // write bitmap data
\r
3609 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3610 fputc(pData[i], diagFile);
\r
3614 SelectObject(tmphdc, oldBitmap);
\r
3616 /* Massive cleanup */
\r
3617 for (x = 0; x < num_clips; x++)
\r
3618 DeleteObject(clips[x]);
\r
3621 DeleteObject(bufferBitmap);
\r
3624 ReleaseDC(hwndMain, hdc);
\r
3626 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3628 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3630 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3633 /* CopyBoard(lastDrawn, board);*/
\r
3634 lastDrawnHighlight = highlightInfo;
\r
3635 lastDrawnPremove = premoveHighlightInfo;
\r
3636 lastDrawnFlipView = flipView;
\r
3637 lastDrawnValid[nr] = 1;
\r
3640 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3645 saveDiagFlag = 1; diagFile = f;
\r
3646 HDCDrawPosition(NULL, TRUE, NULL);
\r
3650 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3657 /*---------------------------------------------------------------------------*\
\r
3658 | CLIENT PAINT PROCEDURE
\r
3659 | This is the main event-handler for the WM_PAINT message.
\r
3661 \*---------------------------------------------------------------------------*/
\r
3663 PaintProc(HWND hwnd)
\r
3669 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3670 if (IsIconic(hwnd)) {
\r
3671 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3673 if (!appData.monoMode) {
\r
3674 SelectPalette(hdc, hPal, FALSE);
\r
3675 RealizePalette(hdc);
\r
3677 HDCDrawPosition(hdc, 1, NULL);
\r
3678 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3679 flipView = !flipView; partnerUp = !partnerUp;
\r
3680 HDCDrawPosition(hdc, 1, NULL);
\r
3681 flipView = !flipView; partnerUp = !partnerUp;
\r
3684 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3685 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3686 ETO_CLIPPED|ETO_OPAQUE,
\r
3687 &messageRect, messageText, strlen(messageText), NULL);
\r
3688 SelectObject(hdc, oldFont);
\r
3689 DisplayBothClocks();
\r
3691 EndPaint(hwnd,&ps);
\r
3699 * If the user selects on a border boundary, return -1; if off the board,
\r
3700 * return -2. Otherwise map the event coordinate to the square.
\r
3701 * The offset boardRect.left or boardRect.top must already have been
\r
3702 * subtracted from x.
\r
3704 int EventToSquare(x, limit)
\r
3712 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3714 x /= (squareSize + lineGap);
\r
3726 DropEnable dropEnables[] = {
\r
3727 { 'P', DP_Pawn, "Pawn" },
\r
3728 { 'N', DP_Knight, "Knight" },
\r
3729 { 'B', DP_Bishop, "Bishop" },
\r
3730 { 'R', DP_Rook, "Rook" },
\r
3731 { 'Q', DP_Queen, "Queen" },
\r
3735 SetupDropMenu(HMENU hmenu)
\r
3737 int i, count, enable;
\r
3739 extern char white_holding[], black_holding[];
\r
3740 char item[MSG_SIZ];
\r
3742 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3743 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3744 dropEnables[i].piece);
\r
3746 while (p && *p++ == dropEnables[i].piece) count++;
\r
3747 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3748 enable = count > 0 || !appData.testLegality
\r
3749 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3750 && !appData.icsActive);
\r
3751 ModifyMenu(hmenu, dropEnables[i].command,
\r
3752 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3753 dropEnables[i].command, item);
\r
3757 void DragPieceBegin(int x, int y)
\r
3759 dragInfo.lastpos.x = boardRect.left + x;
\r
3760 dragInfo.lastpos.y = boardRect.top + y;
\r
3761 dragInfo.from.x = fromX;
\r
3762 dragInfo.from.y = fromY;
\r
3763 dragInfo.start = dragInfo.from;
\r
3764 SetCapture(hwndMain);
\r
3767 void DragPieceEnd(int x, int y)
\r
3770 dragInfo.start.x = dragInfo.start.y = -1;
\r
3771 dragInfo.from = dragInfo.start;
\r
3772 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3775 /* Event handler for mouse messages */
\r
3777 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3781 static int recursive = 0;
\r
3783 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3786 if (message == WM_MBUTTONUP) {
\r
3787 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3788 to the middle button: we simulate pressing the left button too!
\r
3790 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3791 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3797 pt.x = LOWORD(lParam);
\r
3798 pt.y = HIWORD(lParam);
\r
3799 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3800 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3801 if (!flipView && y >= 0) {
\r
3802 y = BOARD_HEIGHT - 1 - y;
\r
3804 if (flipView && x >= 0) {
\r
3805 x = BOARD_WIDTH - 1 - x;
\r
3808 switch (message) {
\r
3809 case WM_LBUTTONDOWN:
\r
3810 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3811 if (gameMode == EditPosition) {
\r
3812 SetWhiteToPlayEvent();
\r
3813 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3814 AdjustClock(flipClock, -1);
\r
3815 } else if (gameMode == IcsPlayingBlack ||
\r
3816 gameMode == MachinePlaysWhite) {
\r
3819 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3820 if (gameMode == EditPosition) {
\r
3821 SetBlackToPlayEvent();
\r
3822 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3823 AdjustClock(!flipClock, -1);
\r
3824 } else if (gameMode == IcsPlayingWhite ||
\r
3825 gameMode == MachinePlaysBlack) {
\r
3829 dragInfo.start.x = dragInfo.start.y = -1;
\r
3830 dragInfo.from = dragInfo.start;
\r
3831 if(fromX == -1 && frozen) { // not sure where this is for
\r
3832 fromX = fromY = -1;
\r
3833 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3836 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3837 DrawPosition(TRUE, NULL);
\r
3840 case WM_LBUTTONUP:
\r
3841 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3842 DrawPosition(TRUE, NULL);
\r
3845 case WM_MOUSEMOVE:
\r
3846 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3847 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3848 if ((appData.animateDragging || appData.highlightDragging)
\r
3849 && (wParam & MK_LBUTTON)
\r
3850 && dragInfo.from.x >= 0)
\r
3852 BOOL full_repaint = FALSE;
\r
3854 if (appData.animateDragging) {
\r
3855 dragInfo.pos = pt;
\r
3857 if (appData.highlightDragging) {
\r
3858 SetHighlights(fromX, fromY, x, y);
\r
3859 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3860 full_repaint = TRUE;
\r
3864 DrawPosition( full_repaint, NULL);
\r
3866 dragInfo.lastpos = dragInfo.pos;
\r
3870 case WM_MOUSEWHEEL: // [DM]
\r
3871 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3872 /* Mouse Wheel is being rolled forward
\r
3873 * Play moves forward
\r
3875 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
3876 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3877 /* Mouse Wheel is being rolled backward
\r
3878 * Play moves backward
\r
3880 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
3881 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3885 case WM_MBUTTONUP:
\r
3886 case WM_RBUTTONUP:
\r
3888 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3891 case WM_MBUTTONDOWN:
\r
3892 case WM_RBUTTONDOWN:
\r
3895 fromX = fromY = -1;
\r
3896 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3897 dragInfo.start.x = dragInfo.start.y = -1;
\r
3898 dragInfo.from = dragInfo.start;
\r
3899 dragInfo.lastpos = dragInfo.pos;
\r
3900 if (appData.highlightDragging) {
\r
3901 ClearHighlights();
\r
3904 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3905 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3906 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
3907 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3908 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
3912 DrawPosition(TRUE, NULL);
\r
3914 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3917 if (message == WM_MBUTTONDOWN) {
\r
3918 buttonCount = 3; /* even if system didn't think so */
\r
3919 if (wParam & MK_SHIFT)
\r
3920 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3922 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3923 } else { /* message == WM_RBUTTONDOWN */
\r
3924 /* Just have one menu, on the right button. Windows users don't
\r
3925 think to try the middle one, and sometimes other software steals
\r
3926 it, or it doesn't really exist. */
\r
3927 if(gameInfo.variant != VariantShogi)
\r
3928 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3930 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3934 SetCapture(hwndMain);
3937 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3938 SetupDropMenu(hmenu);
\r
3939 MenuPopup(hwnd, pt, hmenu, -1);
\r
3949 /* Preprocess messages for buttons in main window */
\r
3951 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3953 int id = GetWindowLong(hwnd, GWL_ID);
\r
3956 for (i=0; i<N_BUTTONS; i++) {
\r
3957 if (buttonDesc[i].id == id) break;
\r
3959 if (i == N_BUTTONS) return 0;
\r
3960 switch (message) {
\r
3965 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3966 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3973 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3976 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3977 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3978 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3979 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3981 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3983 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3984 PopUpMoveDialog((char)wParam);
\r
3990 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3993 /* Process messages for Promotion dialog box */
\r
3995 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3999 switch (message) {
\r
4000 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4001 /* Center the dialog over the application window */
\r
4002 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4003 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4004 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4005 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4006 SW_SHOW : SW_HIDE);
\r
4007 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4008 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4009 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4010 PieceToChar(WhiteAngel) != '~') ||
\r
4011 (PieceToChar(BlackAngel) >= 'A' &&
\r
4012 PieceToChar(BlackAngel) != '~') ) ?
\r
4013 SW_SHOW : SW_HIDE);
\r
4014 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4015 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4016 PieceToChar(WhiteMarshall) != '~') ||
\r
4017 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4018 PieceToChar(BlackMarshall) != '~') ) ?
\r
4019 SW_SHOW : SW_HIDE);
\r
4020 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4021 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4022 gameInfo.variant != VariantShogi ?
\r
4023 SW_SHOW : SW_HIDE);
\r
4024 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4025 gameInfo.variant != VariantShogi ?
\r
4026 SW_SHOW : SW_HIDE);
\r
4027 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4028 gameInfo.variant == VariantShogi ?
\r
4029 SW_SHOW : SW_HIDE);
\r
4030 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4031 gameInfo.variant == VariantShogi ?
\r
4032 SW_SHOW : SW_HIDE);
\r
4033 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4034 gameInfo.variant == VariantSuper ?
\r
4035 SW_SHOW : SW_HIDE);
\r
4038 case WM_COMMAND: /* message: received a command */
\r
4039 switch (LOWORD(wParam)) {
\r
4041 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4042 ClearHighlights();
\r
4043 DrawPosition(FALSE, NULL);
\r
4046 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4049 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4052 promoChar = PieceToChar(BlackRook);
\r
4055 promoChar = PieceToChar(BlackBishop);
\r
4057 case PB_Chancellor:
\r
4058 promoChar = PieceToChar(BlackMarshall);
\r
4060 case PB_Archbishop:
\r
4061 promoChar = PieceToChar(BlackAngel);
\r
4064 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4069 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4070 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4071 only show the popup when we are already sure the move is valid or
\r
4072 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4073 will figure out it is a promotion from the promoChar. */
\r
4074 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4075 fromX = fromY = -1;
\r
4076 if (!appData.highlightLastMove) {
\r
4077 ClearHighlights();
\r
4078 DrawPosition(FALSE, NULL);
\r
4085 /* Pop up promotion dialog */
\r
4087 PromotionPopup(HWND hwnd)
\r
4091 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4092 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4093 hwnd, (DLGPROC)lpProc);
\r
4094 FreeProcInstance(lpProc);
\r
4100 DrawPosition(TRUE, NULL);
\r
4101 PromotionPopup(hwndMain);
\r
4104 /* Toggle ShowThinking */
\r
4106 ToggleShowThinking()
\r
4108 appData.showThinking = !appData.showThinking;
\r
4109 ShowThinkingEvent();
\r
4113 LoadGameDialog(HWND hwnd, char* title)
\r
4117 char fileTitle[MSG_SIZ];
\r
4118 f = OpenFileDialog(hwnd, "rb", "",
\r
4119 appData.oldSaveStyle ? "gam" : "pgn",
\r
4121 title, &number, fileTitle, NULL);
\r
4123 cmailMsgLoaded = FALSE;
\r
4124 if (number == 0) {
\r
4125 int error = GameListBuild(f);
\r
4127 DisplayError("Cannot build game list", error);
\r
4128 } else if (!ListEmpty(&gameList) &&
\r
4129 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4130 GameListPopUp(f, fileTitle);
\r
4133 GameListDestroy();
\r
4136 LoadGame(f, number, fileTitle, FALSE);
\r
4140 int get_term_width()
\r
4145 HFONT hfont, hold_font;
\r
4150 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4154 // get the text metrics
\r
4155 hdc = GetDC(hText);
\r
4156 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4157 if (consoleCF.dwEffects & CFE_BOLD)
\r
4158 lf.lfWeight = FW_BOLD;
\r
4159 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4160 lf.lfItalic = TRUE;
\r
4161 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4162 lf.lfStrikeOut = TRUE;
\r
4163 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4164 lf.lfUnderline = TRUE;
\r
4165 hfont = CreateFontIndirect(&lf);
\r
4166 hold_font = SelectObject(hdc, hfont);
\r
4167 GetTextMetrics(hdc, &tm);
\r
4168 SelectObject(hdc, hold_font);
\r
4169 DeleteObject(hfont);
\r
4170 ReleaseDC(hText, hdc);
\r
4172 // get the rectangle
\r
4173 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4175 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4178 void UpdateICSWidth(HWND hText)
\r
4180 LONG old_width, new_width;
\r
4182 new_width = get_term_width(hText, FALSE);
\r
4183 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4184 if (new_width != old_width)
\r
4186 ics_update_width(new_width);
\r
4187 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4192 ChangedConsoleFont()
\r
4195 CHARRANGE tmpsel, sel;
\r
4196 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4197 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4198 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4201 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4202 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4203 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4204 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4205 * size. This was undocumented in the version of MSVC++ that I had
\r
4206 * when I wrote the code, but is apparently documented now.
\r
4208 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4209 cfmt.bCharSet = f->lf.lfCharSet;
\r
4210 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4211 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4212 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4213 /* Why are the following seemingly needed too? */
\r
4214 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4215 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4216 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4218 tmpsel.cpMax = -1; /*999999?*/
\r
4219 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4220 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4221 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4222 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4224 paraf.cbSize = sizeof(paraf);
\r
4225 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4226 paraf.dxStartIndent = 0;
\r
4227 paraf.dxOffset = WRAP_INDENT;
\r
4228 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4229 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4230 UpdateICSWidth(hText);
\r
4233 /*---------------------------------------------------------------------------*\
\r
4235 * Window Proc for main window
\r
4237 \*---------------------------------------------------------------------------*/
\r
4239 /* Process messages for main window, etc. */
\r
4241 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4244 int wmId, wmEvent;
\r
4248 char fileTitle[MSG_SIZ];
\r
4249 char buf[MSG_SIZ];
\r
4250 static SnapData sd;
\r
4252 switch (message) {
\r
4254 case WM_PAINT: /* message: repaint portion of window */
\r
4258 case WM_ERASEBKGND:
\r
4259 if (IsIconic(hwnd)) {
\r
4260 /* Cheat; change the message */
\r
4261 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4263 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4267 case WM_LBUTTONDOWN:
\r
4268 case WM_MBUTTONDOWN:
\r
4269 case WM_RBUTTONDOWN:
\r
4270 case WM_LBUTTONUP:
\r
4271 case WM_MBUTTONUP:
\r
4272 case WM_RBUTTONUP:
\r
4273 case WM_MOUSEMOVE:
\r
4274 case WM_MOUSEWHEEL:
\r
4275 MouseEvent(hwnd, message, wParam, lParam);
\r
4278 JAWS_KB_NAVIGATION
\r
4282 JAWS_ALT_INTERCEPT
\r
4284 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4285 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4286 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4287 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4289 SendMessage(h, message, wParam, lParam);
\r
4290 } else if(lParam != KF_REPEAT) {
\r
4291 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4292 PopUpMoveDialog((char)wParam);
\r
4293 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4294 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4299 case WM_PALETTECHANGED:
\r
4300 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4302 HDC hdc = GetDC(hwndMain);
\r
4303 SelectPalette(hdc, hPal, TRUE);
\r
4304 nnew = RealizePalette(hdc);
\r
4306 paletteChanged = TRUE;
\r
4307 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4309 ReleaseDC(hwnd, hdc);
\r
4313 case WM_QUERYNEWPALETTE:
\r
4314 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4316 HDC hdc = GetDC(hwndMain);
\r
4317 paletteChanged = FALSE;
\r
4318 SelectPalette(hdc, hPal, FALSE);
\r
4319 nnew = RealizePalette(hdc);
\r
4321 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4323 ReleaseDC(hwnd, hdc);
\r
4328 case WM_COMMAND: /* message: command from application menu */
\r
4329 wmId = LOWORD(wParam);
\r
4330 wmEvent = HIWORD(wParam);
\r
4335 SAY("new game enter a move to play against the computer with white");
\r
4338 case IDM_NewGameFRC:
\r
4339 if( NewGameFRC() == 0 ) {
\r
4344 case IDM_NewVariant:
\r
4345 NewVariantPopup(hwnd);
\r
4348 case IDM_LoadGame:
\r
4349 LoadGameDialog(hwnd, "Load Game from File");
\r
4352 case IDM_LoadNextGame:
\r
4356 case IDM_LoadPrevGame:
\r
4360 case IDM_ReloadGame:
\r
4364 case IDM_LoadPosition:
\r
4365 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4366 Reset(FALSE, TRUE);
\r
4369 f = OpenFileDialog(hwnd, "rb", "",
\r
4370 appData.oldSaveStyle ? "pos" : "fen",
\r
4372 "Load Position from File", &number, fileTitle, NULL);
\r
4374 LoadPosition(f, number, fileTitle);
\r
4378 case IDM_LoadNextPosition:
\r
4379 ReloadPosition(1);
\r
4382 case IDM_LoadPrevPosition:
\r
4383 ReloadPosition(-1);
\r
4386 case IDM_ReloadPosition:
\r
4387 ReloadPosition(0);
\r
4390 case IDM_SaveGame:
\r
4391 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4392 f = OpenFileDialog(hwnd, "a", defName,
\r
4393 appData.oldSaveStyle ? "gam" : "pgn",
\r
4395 "Save Game to File", NULL, fileTitle, NULL);
\r
4397 SaveGame(f, 0, "");
\r
4401 case IDM_SavePosition:
\r
4402 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4403 f = OpenFileDialog(hwnd, "a", defName,
\r
4404 appData.oldSaveStyle ? "pos" : "fen",
\r
4406 "Save Position to File", NULL, fileTitle, NULL);
\r
4408 SavePosition(f, 0, "");
\r
4412 case IDM_SaveDiagram:
\r
4413 defName = "diagram";
\r
4414 f = OpenFileDialog(hwnd, "wb", defName,
\r
4417 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4423 case IDM_CopyGame:
\r
4424 CopyGameToClipboard();
\r
4427 case IDM_PasteGame:
\r
4428 PasteGameFromClipboard();
\r
4431 case IDM_CopyGameListToClipboard:
\r
4432 CopyGameListToClipboard();
\r
4435 /* [AS] Autodetect FEN or PGN data */
\r
4436 case IDM_PasteAny:
\r
4437 PasteGameOrFENFromClipboard();
\r
4440 /* [AS] Move history */
\r
4441 case IDM_ShowMoveHistory:
\r
4442 if( MoveHistoryIsUp() ) {
\r
4443 MoveHistoryPopDown();
\r
4446 MoveHistoryPopUp();
\r
4450 /* [AS] Eval graph */
\r
4451 case IDM_ShowEvalGraph:
\r
4452 if( EvalGraphIsUp() ) {
\r
4453 EvalGraphPopDown();
\r
4457 SetFocus(hwndMain);
\r
4461 /* [AS] Engine output */
\r
4462 case IDM_ShowEngineOutput:
\r
4463 if( EngineOutputIsUp() ) {
\r
4464 EngineOutputPopDown();
\r
4467 EngineOutputPopUp();
\r
4471 /* [AS] User adjudication */
\r
4472 case IDM_UserAdjudication_White:
\r
4473 UserAdjudicationEvent( +1 );
\r
4476 case IDM_UserAdjudication_Black:
\r
4477 UserAdjudicationEvent( -1 );
\r
4480 case IDM_UserAdjudication_Draw:
\r
4481 UserAdjudicationEvent( 0 );
\r
4484 /* [AS] Game list options dialog */
\r
4485 case IDM_GameListOptions:
\r
4486 GameListOptions();
\r
4493 case IDM_CopyPosition:
\r
4494 CopyFENToClipboard();
\r
4497 case IDM_PastePosition:
\r
4498 PasteFENFromClipboard();
\r
4501 case IDM_MailMove:
\r
4505 case IDM_ReloadCMailMsg:
\r
4506 Reset(TRUE, TRUE);
\r
4507 ReloadCmailMsgEvent(FALSE);
\r
4510 case IDM_Minimize:
\r
4511 ShowWindow(hwnd, SW_MINIMIZE);
\r
4518 case IDM_MachineWhite:
\r
4519 MachineWhiteEvent();
\r
4521 * refresh the tags dialog only if it's visible
\r
4523 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4525 tags = PGNTags(&gameInfo);
\r
4526 TagsPopUp(tags, CmailMsg());
\r
4529 SAY("computer starts playing white");
\r
4532 case IDM_MachineBlack:
\r
4533 MachineBlackEvent();
\r
4535 * refresh the tags dialog only if it's visible
\r
4537 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4539 tags = PGNTags(&gameInfo);
\r
4540 TagsPopUp(tags, CmailMsg());
\r
4543 SAY("computer starts playing black");
\r
4546 case IDM_TwoMachines:
\r
4547 TwoMachinesEvent();
\r
4549 * refresh the tags dialog only if it's visible
\r
4551 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4553 tags = PGNTags(&gameInfo);
\r
4554 TagsPopUp(tags, CmailMsg());
\r
4557 SAY("computer starts playing both sides");
\r
4560 case IDM_AnalysisMode:
\r
4561 if (!first.analysisSupport) {
\r
4562 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4563 DisplayError(buf, 0);
\r
4565 SAY("analyzing current position");
\r
4566 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4567 if (appData.icsActive) {
\r
4568 if (gameMode != IcsObserving) {
\r
4569 sprintf(buf, "You are not observing a game");
\r
4570 DisplayError(buf, 0);
\r
4571 /* secure check */
\r
4572 if (appData.icsEngineAnalyze) {
\r
4573 if (appData.debugMode)
\r
4574 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4575 ExitAnalyzeMode();
\r
4581 /* if enable, user want disable icsEngineAnalyze */
\r
4582 if (appData.icsEngineAnalyze) {
\r
4583 ExitAnalyzeMode();
\r
4587 appData.icsEngineAnalyze = TRUE;
\r
4588 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4591 if (!appData.showThinking) ToggleShowThinking();
\r
4592 AnalyzeModeEvent();
\r
4596 case IDM_AnalyzeFile:
\r
4597 if (!first.analysisSupport) {
\r
4598 char buf[MSG_SIZ];
\r
4599 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4600 DisplayError(buf, 0);
\r
4602 if (!appData.showThinking) ToggleShowThinking();
\r
4603 AnalyzeFileEvent();
\r
4604 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4605 AnalysisPeriodicEvent(1);
\r
4609 case IDM_IcsClient:
\r
4613 case IDM_EditGame:
\r
4618 case IDM_EditPosition:
\r
4619 EditPositionEvent();
\r
4620 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4623 case IDM_Training:
\r
4627 case IDM_ShowGameList:
\r
4628 ShowGameListProc();
\r
4631 case IDM_EditTags:
\r
4635 case IDM_EditComment:
\r
4636 if (commentUp && editComment) {
\r
4639 EditCommentEvent();
\r
4659 case IDM_CallFlag:
\r
4679 case IDM_StopObserving:
\r
4680 StopObservingEvent();
\r
4683 case IDM_StopExamining:
\r
4684 StopExaminingEvent();
\r
4688 UploadGameEvent();
\r
4691 case IDM_TypeInMove:
\r
4692 PopUpMoveDialog('\000');
\r
4695 case IDM_TypeInName:
\r
4696 PopUpNameDialog('\000');
\r
4699 case IDM_Backward:
\r
4701 SetFocus(hwndMain);
\r
4708 SetFocus(hwndMain);
\r
4713 SetFocus(hwndMain);
\r
4718 SetFocus(hwndMain);
\r
4722 RevertEvent(FALSE);
\r
4725 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4726 RevertEvent(TRUE);
\r
4729 case IDM_TruncateGame:
\r
4730 TruncateGameEvent();
\r
4737 case IDM_RetractMove:
\r
4738 RetractMoveEvent();
\r
4741 case IDM_FlipView:
\r
4742 flipView = !flipView;
\r
4743 DrawPosition(FALSE, NULL);
\r
4746 case IDM_FlipClock:
\r
4747 flipClock = !flipClock;
\r
4748 DisplayBothClocks();
\r
4749 DrawPosition(FALSE, NULL);
\r
4752 case IDM_MuteSounds:
\r
4753 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4754 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4755 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4758 case IDM_GeneralOptions:
\r
4759 GeneralOptionsPopup(hwnd);
\r
4760 DrawPosition(TRUE, NULL);
\r
4763 case IDM_BoardOptions:
\r
4764 BoardOptionsPopup(hwnd);
\r
4767 case IDM_EnginePlayOptions:
\r
4768 EnginePlayOptionsPopup(hwnd);
\r
4771 case IDM_Engine1Options:
\r
4772 EngineOptionsPopup(hwnd, &first);
\r
4775 case IDM_Engine2Options:
\r
4776 EngineOptionsPopup(hwnd, &second);
\r
4779 case IDM_OptionsUCI:
\r
4780 UciOptionsPopup(hwnd);
\r
4783 case IDM_IcsOptions:
\r
4784 IcsOptionsPopup(hwnd);
\r
4788 FontsOptionsPopup(hwnd);
\r
4792 SoundOptionsPopup(hwnd);
\r
4795 case IDM_CommPort:
\r
4796 CommPortOptionsPopup(hwnd);
\r
4799 case IDM_LoadOptions:
\r
4800 LoadOptionsPopup(hwnd);
\r
4803 case IDM_SaveOptions:
\r
4804 SaveOptionsPopup(hwnd);
\r
4807 case IDM_TimeControl:
\r
4808 TimeControlOptionsPopup(hwnd);
\r
4811 case IDM_SaveSettings:
\r
4812 SaveSettings(settingsFileName);
\r
4815 case IDM_SaveSettingsOnExit:
\r
4816 saveSettingsOnExit = !saveSettingsOnExit;
\r
4817 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4818 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4819 MF_CHECKED : MF_UNCHECKED));
\r
4830 case IDM_AboutGame:
\r
4835 appData.debugMode = !appData.debugMode;
\r
4836 if (appData.debugMode) {
\r
4837 char dir[MSG_SIZ];
\r
4838 GetCurrentDirectory(MSG_SIZ, dir);
\r
4839 SetCurrentDirectory(installDir);
\r
4840 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4841 SetCurrentDirectory(dir);
\r
4842 setbuf(debugFP, NULL);
\r
4849 case IDM_HELPCONTENTS:
\r
4850 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4851 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4852 MessageBox (GetFocus(),
\r
4853 "Unable to activate help",
\r
4854 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4858 case IDM_HELPSEARCH:
\r
4859 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4860 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4861 MessageBox (GetFocus(),
\r
4862 "Unable to activate help",
\r
4863 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4867 case IDM_HELPHELP:
\r
4868 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4869 MessageBox (GetFocus(),
\r
4870 "Unable to activate help",
\r
4871 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4876 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4878 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4879 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4880 FreeProcInstance(lpProc);
\r
4883 case IDM_DirectCommand1:
\r
4884 AskQuestionEvent("Direct Command",
\r
4885 "Send to chess program:", "", "1");
\r
4887 case IDM_DirectCommand2:
\r
4888 AskQuestionEvent("Direct Command",
\r
4889 "Send to second chess program:", "", "2");
\r
4892 case EP_WhitePawn:
\r
4893 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4894 fromX = fromY = -1;
\r
4897 case EP_WhiteKnight:
\r
4898 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4899 fromX = fromY = -1;
\r
4902 case EP_WhiteBishop:
\r
4903 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4904 fromX = fromY = -1;
\r
4907 case EP_WhiteRook:
\r
4908 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4909 fromX = fromY = -1;
\r
4912 case EP_WhiteQueen:
\r
4913 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4914 fromX = fromY = -1;
\r
4917 case EP_WhiteFerz:
\r
4918 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4919 fromX = fromY = -1;
\r
4922 case EP_WhiteWazir:
\r
4923 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4924 fromX = fromY = -1;
\r
4927 case EP_WhiteAlfil:
\r
4928 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4929 fromX = fromY = -1;
\r
4932 case EP_WhiteCannon:
\r
4933 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4934 fromX = fromY = -1;
\r
4937 case EP_WhiteCardinal:
\r
4938 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4939 fromX = fromY = -1;
\r
4942 case EP_WhiteMarshall:
\r
4943 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4944 fromX = fromY = -1;
\r
4947 case EP_WhiteKing:
\r
4948 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4949 fromX = fromY = -1;
\r
4952 case EP_BlackPawn:
\r
4953 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4954 fromX = fromY = -1;
\r
4957 case EP_BlackKnight:
\r
4958 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4959 fromX = fromY = -1;
\r
4962 case EP_BlackBishop:
\r
4963 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4964 fromX = fromY = -1;
\r
4967 case EP_BlackRook:
\r
4968 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4969 fromX = fromY = -1;
\r
4972 case EP_BlackQueen:
\r
4973 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4974 fromX = fromY = -1;
\r
4977 case EP_BlackFerz:
\r
4978 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4979 fromX = fromY = -1;
\r
4982 case EP_BlackWazir:
\r
4983 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4984 fromX = fromY = -1;
\r
4987 case EP_BlackAlfil:
\r
4988 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4989 fromX = fromY = -1;
\r
4992 case EP_BlackCannon:
\r
4993 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4994 fromX = fromY = -1;
\r
4997 case EP_BlackCardinal:
\r
4998 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4999 fromX = fromY = -1;
\r
5002 case EP_BlackMarshall:
\r
5003 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5004 fromX = fromY = -1;
\r
5007 case EP_BlackKing:
\r
5008 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5009 fromX = fromY = -1;
\r
5012 case EP_EmptySquare:
\r
5013 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5014 fromX = fromY = -1;
\r
5017 case EP_ClearBoard:
\r
5018 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5019 fromX = fromY = -1;
\r
5023 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5024 fromX = fromY = -1;
\r
5028 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5029 fromX = fromY = -1;
\r
5033 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5034 fromX = fromY = -1;
\r
5038 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5039 fromX = fromY = -1;
\r
5043 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5044 fromX = fromY = -1;
\r
5048 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5049 fromX = fromY = -1;
\r
5053 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5054 fromX = fromY = -1;
\r
5058 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5059 fromX = fromY = -1;
\r
5063 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5064 fromX = fromY = -1;
\r
5068 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5074 case CLOCK_TIMER_ID:
\r
5075 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5076 clockTimerEvent = 0;
\r
5077 DecrementClocks(); /* call into back end */
\r
5079 case LOAD_GAME_TIMER_ID:
\r
5080 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5081 loadGameTimerEvent = 0;
\r
5082 AutoPlayGameLoop(); /* call into back end */
\r
5084 case ANALYSIS_TIMER_ID:
\r
5085 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5086 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5087 AnalysisPeriodicEvent(0);
\r
5089 KillTimer(hwnd, analysisTimerEvent);
\r
5090 analysisTimerEvent = 0;
\r
5093 case DELAYED_TIMER_ID:
\r
5094 KillTimer(hwnd, delayedTimerEvent);
\r
5095 delayedTimerEvent = 0;
\r
5096 delayedTimerCallback();
\r
5101 case WM_USER_Input:
\r
5102 InputEvent(hwnd, message, wParam, lParam);
\r
5105 /* [AS] Also move "attached" child windows */
\r
5106 case WM_WINDOWPOSCHANGING:
\r
5108 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5109 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5111 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5112 /* Window is moving */
\r
5115 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5116 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5117 rcMain.right = wpMain.x + wpMain.width;
\r
5118 rcMain.top = wpMain.y;
\r
5119 rcMain.bottom = wpMain.y + wpMain.height;
\r
5121 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5122 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5123 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5124 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5125 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5126 wpMain.x = lpwp->x;
\r
5127 wpMain.y = lpwp->y;
\r
5132 /* [AS] Snapping */
\r
5133 case WM_ENTERSIZEMOVE:
\r
5134 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5135 if (hwnd == hwndMain) {
\r
5136 doingSizing = TRUE;
\r
5139 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5143 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5144 if (hwnd == hwndMain) {
\r
5145 lastSizing = wParam;
\r
5150 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5151 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5153 case WM_EXITSIZEMOVE:
\r
5154 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5155 if (hwnd == hwndMain) {
\r
5157 doingSizing = FALSE;
\r
5158 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5159 GetClientRect(hwnd, &client);
\r
5160 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5162 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5164 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5167 case WM_DESTROY: /* message: window being destroyed */
\r
5168 PostQuitMessage(0);
\r
5172 if (hwnd == hwndMain) {
\r
5177 default: /* Passes it on if unprocessed */
\r
5178 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5183 /*---------------------------------------------------------------------------*\
\r
5185 * Misc utility routines
\r
5187 \*---------------------------------------------------------------------------*/
\r
5190 * Decent random number generator, at least not as bad as Windows
\r
5191 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5193 unsigned int randstate;
\r
5198 randstate = randstate * 1664525 + 1013904223;
\r
5199 return (int) randstate & 0x7fffffff;
\r
5203 mysrandom(unsigned int seed)
\r
5210 * returns TRUE if user selects a different color, FALSE otherwise
\r
5214 ChangeColor(HWND hwnd, COLORREF *which)
\r
5216 static BOOL firstTime = TRUE;
\r
5217 static DWORD customColors[16];
\r
5219 COLORREF newcolor;
\r
5224 /* Make initial colors in use available as custom colors */
\r
5225 /* Should we put the compiled-in defaults here instead? */
\r
5227 customColors[i++] = lightSquareColor & 0xffffff;
\r
5228 customColors[i++] = darkSquareColor & 0xffffff;
\r
5229 customColors[i++] = whitePieceColor & 0xffffff;
\r
5230 customColors[i++] = blackPieceColor & 0xffffff;
\r
5231 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5232 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5234 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5235 customColors[i++] = textAttribs[ccl].color;
\r
5237 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5238 firstTime = FALSE;
\r
5241 cc.lStructSize = sizeof(cc);
\r
5242 cc.hwndOwner = hwnd;
\r
5243 cc.hInstance = NULL;
\r
5244 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5245 cc.lpCustColors = (LPDWORD) customColors;
\r
5246 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5248 if (!ChooseColor(&cc)) return FALSE;
\r
5250 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5251 if (newcolor == *which) return FALSE;
\r
5252 *which = newcolor;
\r
5256 InitDrawingColors();
\r
5257 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5262 MyLoadSound(MySound *ms)
\r
5268 if (ms->data) free(ms->data);
\r
5271 switch (ms->name[0]) {
\r
5277 /* System sound from Control Panel. Don't preload here. */
\r
5281 if (ms->name[1] == NULLCHAR) {
\r
5282 /* "!" alone = silence */
\r
5285 /* Builtin wave resource. Error if not found. */
\r
5286 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5287 if (h == NULL) break;
\r
5288 ms->data = (void *)LoadResource(hInst, h);
\r
5289 if (h == NULL) break;
\r
5294 /* .wav file. Error if not found. */
\r
5295 f = fopen(ms->name, "rb");
\r
5296 if (f == NULL) break;
\r
5297 if (fstat(fileno(f), &st) < 0) break;
\r
5298 ms->data = malloc(st.st_size);
\r
5299 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5305 char buf[MSG_SIZ];
\r
5306 sprintf(buf, "Error loading sound %s", ms->name);
\r
5307 DisplayError(buf, GetLastError());
\r
5313 MyPlaySound(MySound *ms)
\r
5315 BOOLEAN ok = FALSE;
\r
5317 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5318 switch (ms->name[0]) {
\r
5320 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5325 /* System sound from Control Panel (deprecated feature).
\r
5326 "$" alone or an unset sound name gets default beep (still in use). */
\r
5327 if (ms->name[1]) {
\r
5328 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5330 if (!ok) ok = MessageBeep(MB_OK);
\r
5333 /* Builtin wave resource, or "!" alone for silence */
\r
5334 if (ms->name[1]) {
\r
5335 if (ms->data == NULL) return FALSE;
\r
5336 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5342 /* .wav file. Error if not found. */
\r
5343 if (ms->data == NULL) return FALSE;
\r
5344 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5347 /* Don't print an error: this can happen innocently if the sound driver
\r
5348 is busy; for instance, if another instance of WinBoard is playing
\r
5349 a sound at about the same time. */
\r
5355 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5358 OPENFILENAME *ofn;
\r
5359 static UINT *number; /* gross that this is static */
\r
5361 switch (message) {
\r
5362 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5363 /* Center the dialog over the application window */
\r
5364 ofn = (OPENFILENAME *) lParam;
\r
5365 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5366 number = (UINT *) ofn->lCustData;
\r
5367 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5371 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5372 return FALSE; /* Allow for further processing */
\r
5375 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5376 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5378 return FALSE; /* Allow for further processing */
\r
5384 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5386 static UINT *number;
\r
5387 OPENFILENAME *ofname;
\r
5390 case WM_INITDIALOG:
\r
5391 ofname = (OPENFILENAME *)lParam;
\r
5392 number = (UINT *)(ofname->lCustData);
\r
5395 ofnot = (OFNOTIFY *)lParam;
\r
5396 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5397 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5406 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5407 char *nameFilt, char *dlgTitle, UINT *number,
\r
5408 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5410 OPENFILENAME openFileName;
\r
5411 char buf1[MSG_SIZ];
\r
5414 if (fileName == NULL) fileName = buf1;
\r
5415 if (defName == NULL) {
\r
5416 strcpy(fileName, "*.");
\r
5417 strcat(fileName, defExt);
\r
5419 strcpy(fileName, defName);
\r
5421 if (fileTitle) strcpy(fileTitle, "");
\r
5422 if (number) *number = 0;
\r
5424 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5425 openFileName.hwndOwner = hwnd;
\r
5426 openFileName.hInstance = (HANDLE) hInst;
\r
5427 openFileName.lpstrFilter = nameFilt;
\r
5428 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5429 openFileName.nMaxCustFilter = 0L;
\r
5430 openFileName.nFilterIndex = 1L;
\r
5431 openFileName.lpstrFile = fileName;
\r
5432 openFileName.nMaxFile = MSG_SIZ;
\r
5433 openFileName.lpstrFileTitle = fileTitle;
\r
5434 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5435 openFileName.lpstrInitialDir = NULL;
\r
5436 openFileName.lpstrTitle = dlgTitle;
\r
5437 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5438 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5439 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5440 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5441 openFileName.nFileOffset = 0;
\r
5442 openFileName.nFileExtension = 0;
\r
5443 openFileName.lpstrDefExt = defExt;
\r
5444 openFileName.lCustData = (LONG) number;
\r
5445 openFileName.lpfnHook = oldDialog ?
\r
5446 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5447 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5449 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5450 GetOpenFileName(&openFileName)) {
\r
5451 /* open the file */
\r
5452 f = fopen(openFileName.lpstrFile, write);
\r
5454 MessageBox(hwnd, "File open failed", NULL,
\r
5455 MB_OK|MB_ICONEXCLAMATION);
\r
5459 int err = CommDlgExtendedError();
\r
5460 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5469 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5471 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5474 * Get the first pop-up menu in the menu template. This is the
\r
5475 * menu that TrackPopupMenu displays.
\r
5477 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5479 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5482 * TrackPopup uses screen coordinates, so convert the
\r
5483 * coordinates of the mouse click to screen coordinates.
\r
5485 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5487 /* Draw and track the floating pop-up menu. */
\r
5488 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5489 pt.x, pt.y, 0, hwnd, NULL);
\r
5491 /* Destroy the menu.*/
\r
5492 DestroyMenu(hmenu);
\r
5497 int sizeX, sizeY, newSizeX, newSizeY;
\r
5499 } ResizeEditPlusButtonsClosure;
\r
5502 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5504 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5508 if (hChild == cl->hText) return TRUE;
\r
5509 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5510 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5511 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5512 ScreenToClient(cl->hDlg, &pt);
\r
5513 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5514 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5518 /* Resize a dialog that has a (rich) edit field filling most of
\r
5519 the top, with a row of buttons below */
\r
5521 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5524 int newTextHeight, newTextWidth;
\r
5525 ResizeEditPlusButtonsClosure cl;
\r
5527 /*if (IsIconic(hDlg)) return;*/
\r
5528 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5530 cl.hdwp = BeginDeferWindowPos(8);
\r
5532 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5533 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5534 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5535 if (newTextHeight < 0) {
\r
5536 newSizeY += -newTextHeight;
\r
5537 newTextHeight = 0;
\r
5539 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5540 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5546 cl.newSizeX = newSizeX;
\r
5547 cl.newSizeY = newSizeY;
\r
5548 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5550 EndDeferWindowPos(cl.hdwp);
\r
5553 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5555 RECT rChild, rParent;
\r
5556 int wChild, hChild, wParent, hParent;
\r
5557 int wScreen, hScreen, xNew, yNew;
\r
5560 /* Get the Height and Width of the child window */
\r
5561 GetWindowRect (hwndChild, &rChild);
\r
5562 wChild = rChild.right - rChild.left;
\r
5563 hChild = rChild.bottom - rChild.top;
\r
5565 /* Get the Height and Width of the parent window */
\r
5566 GetWindowRect (hwndParent, &rParent);
\r
5567 wParent = rParent.right - rParent.left;
\r
5568 hParent = rParent.bottom - rParent.top;
\r
5570 /* Get the display limits */
\r
5571 hdc = GetDC (hwndChild);
\r
5572 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5573 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5574 ReleaseDC(hwndChild, hdc);
\r
5576 /* Calculate new X position, then adjust for screen */
\r
5577 xNew = rParent.left + ((wParent - wChild) /2);
\r
5580 } else if ((xNew+wChild) > wScreen) {
\r
5581 xNew = wScreen - wChild;
\r
5584 /* Calculate new Y position, then adjust for screen */
\r
5586 yNew = rParent.top + ((hParent - hChild) /2);
\r
5589 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5594 } else if ((yNew+hChild) > hScreen) {
\r
5595 yNew = hScreen - hChild;
\r
5598 /* Set it, and return */
\r
5599 return SetWindowPos (hwndChild, NULL,
\r
5600 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5603 /* Center one window over another */
\r
5604 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5606 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5609 /*---------------------------------------------------------------------------*\
\r
5611 * Startup Dialog functions
\r
5613 \*---------------------------------------------------------------------------*/
\r
5615 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5617 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5619 while (*cd != NULL) {
\r
5620 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5626 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5628 char buf1[MAX_ARG_LEN];
\r
5631 if (str[0] == '@') {
\r
5632 FILE* f = fopen(str + 1, "r");
\r
5634 DisplayFatalError(str + 1, errno, 2);
\r
5637 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5639 buf1[len] = NULLCHAR;
\r
5643 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5646 char buf[MSG_SIZ];
\r
5647 char *end = strchr(str, '\n');
\r
5648 if (end == NULL) return;
\r
5649 memcpy(buf, str, end - str);
\r
5650 buf[end - str] = NULLCHAR;
\r
5651 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5657 SetStartupDialogEnables(HWND hDlg)
\r
5659 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5660 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5661 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5662 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5663 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5664 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5665 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5666 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5667 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5668 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5669 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5670 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5671 IsDlgButtonChecked(hDlg, OPT_View));
\r
5675 QuoteForFilename(char *filename)
\r
5677 int dquote, space;
\r
5678 dquote = strchr(filename, '"') != NULL;
\r
5679 space = strchr(filename, ' ') != NULL;
\r
5680 if (dquote || space) {
\r
5692 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5694 char buf[MSG_SIZ];
\r
5697 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5698 q = QuoteForFilename(nthcp);
\r
5699 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5700 if (*nthdir != NULLCHAR) {
\r
5701 q = QuoteForFilename(nthdir);
\r
5702 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5704 if (*nthcp == NULLCHAR) {
\r
5705 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5706 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5707 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5708 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5713 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5715 char buf[MSG_SIZ];
\r
5719 switch (message) {
\r
5720 case WM_INITDIALOG:
\r
5721 /* Center the dialog */
\r
5722 CenterWindow (hDlg, GetDesktopWindow());
\r
5723 /* Initialize the dialog items */
\r
5724 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5725 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5726 firstChessProgramNames);
\r
5727 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5728 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5729 secondChessProgramNames);
\r
5730 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5731 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5732 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5733 if (*appData.icsHelper != NULLCHAR) {
\r
5734 char *q = QuoteForFilename(appData.icsHelper);
\r
5735 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5737 if (*appData.icsHost == NULLCHAR) {
\r
5738 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5739 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5740 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5741 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5742 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5745 if (appData.icsActive) {
\r
5746 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5748 else if (appData.noChessProgram) {
\r
5749 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5752 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5755 SetStartupDialogEnables(hDlg);
\r
5759 switch (LOWORD(wParam)) {
\r
5761 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5762 strcpy(buf, "/fcp=");
\r
5763 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5765 ParseArgs(StringGet, &p);
\r
5766 strcpy(buf, "/scp=");
\r
5767 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5769 ParseArgs(StringGet, &p);
\r
5770 appData.noChessProgram = FALSE;
\r
5771 appData.icsActive = FALSE;
\r
5772 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5773 strcpy(buf, "/ics /icshost=");
\r
5774 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5776 ParseArgs(StringGet, &p);
\r
5777 if (appData.zippyPlay) {
\r
5778 strcpy(buf, "/fcp=");
\r
5779 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5781 ParseArgs(StringGet, &p);
\r
5783 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5784 appData.noChessProgram = TRUE;
\r
5785 appData.icsActive = FALSE;
\r
5787 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5788 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5791 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5792 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5794 ParseArgs(StringGet, &p);
\r
5796 EndDialog(hDlg, TRUE);
\r
5803 case IDM_HELPCONTENTS:
\r
5804 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5805 MessageBox (GetFocus(),
\r
5806 "Unable to activate help",
\r
5807 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5812 SetStartupDialogEnables(hDlg);
\r
5820 /*---------------------------------------------------------------------------*\
\r
5822 * About box dialog functions
\r
5824 \*---------------------------------------------------------------------------*/
\r
5826 /* Process messages for "About" dialog box */
\r
5828 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5830 switch (message) {
\r
5831 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5832 /* Center the dialog over the application window */
\r
5833 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5834 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5838 case WM_COMMAND: /* message: received a command */
\r
5839 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5840 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5841 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5849 /*---------------------------------------------------------------------------*\
\r
5851 * Comment Dialog functions
\r
5853 \*---------------------------------------------------------------------------*/
\r
5856 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5858 static HANDLE hwndText = NULL;
\r
5859 int len, newSizeX, newSizeY, flags;
\r
5860 static int sizeX, sizeY;
\r
5865 switch (message) {
\r
5866 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5867 /* Initialize the dialog items */
\r
5868 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5869 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5870 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5871 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5872 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5873 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5874 SetWindowText(hDlg, commentTitle);
\r
5875 if (editComment) {
\r
5876 SetFocus(hwndText);
\r
5878 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5880 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5881 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5882 MAKELPARAM(FALSE, 0));
\r
5883 /* Size and position the dialog */
\r
5884 if (!commentDialog) {
\r
5885 commentDialog = hDlg;
\r
5886 flags = SWP_NOZORDER;
\r
5887 GetClientRect(hDlg, &rect);
\r
5888 sizeX = rect.right;
\r
5889 sizeY = rect.bottom;
\r
5890 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5891 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5892 WINDOWPLACEMENT wp;
\r
5893 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5894 wp.length = sizeof(WINDOWPLACEMENT);
\r
5896 wp.showCmd = SW_SHOW;
\r
5897 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5898 wp.rcNormalPosition.left = wpComment.x;
\r
5899 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5900 wp.rcNormalPosition.top = wpComment.y;
\r
5901 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5902 SetWindowPlacement(hDlg, &wp);
\r
5904 GetClientRect(hDlg, &rect);
\r
5905 newSizeX = rect.right;
\r
5906 newSizeY = rect.bottom;
\r
5907 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5908 newSizeX, newSizeY);
\r
5913 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5916 case WM_COMMAND: /* message: received a command */
\r
5917 switch (LOWORD(wParam)) {
\r
5919 if (editComment) {
\r
5921 /* Read changed options from the dialog box */
\r
5922 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5923 len = GetWindowTextLength(hwndText);
\r
5924 str = (char *) malloc(len + 1);
\r
5925 GetWindowText(hwndText, str, len + 1);
\r
5934 ReplaceComment(commentIndex, str);
\r
5941 case OPT_CancelComment:
\r
5945 case OPT_ClearComment:
\r
5946 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5949 case OPT_EditComment:
\r
5950 EditCommentEvent();
\r
5958 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5959 if( wParam == OPT_CommentText ) {
\r
5960 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5962 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5966 pt.x = LOWORD( lpMF->lParam );
\r
5967 pt.y = HIWORD( lpMF->lParam );
\r
5969 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5971 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5972 len = GetWindowTextLength(hwndText);
\r
5973 str = (char *) malloc(len + 1);
\r
5974 GetWindowText(hwndText, str, len + 1);
\r
5975 ReplaceComment(commentIndex, str);
\r
5976 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5977 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5980 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5981 lpMF->msg = WM_USER;
\r
5989 newSizeX = LOWORD(lParam);
\r
5990 newSizeY = HIWORD(lParam);
\r
5991 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5996 case WM_GETMINMAXINFO:
\r
5997 /* Prevent resizing window too small */
\r
5998 mmi = (MINMAXINFO *) lParam;
\r
5999 mmi->ptMinTrackSize.x = 100;
\r
6000 mmi->ptMinTrackSize.y = 100;
\r
6007 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6012 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6014 if (str == NULL) str = "";
\r
6015 p = (char *) malloc(2 * strlen(str) + 2);
\r
6018 if (*str == '\n') *q++ = '\r';
\r
6022 if (commentText != NULL) free(commentText);
\r
6024 commentIndex = index;
\r
6025 commentTitle = title;
\r
6027 editComment = edit;
\r
6029 if (commentDialog) {
\r
6030 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6031 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6033 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6034 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6035 hwndMain, (DLGPROC)lpProc);
\r
6036 FreeProcInstance(lpProc);
\r
6042 /*---------------------------------------------------------------------------*\
\r
6044 * Type-in move dialog functions
\r
6046 \*---------------------------------------------------------------------------*/
\r
6049 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6051 char move[MSG_SIZ];
\r
6053 ChessMove moveType;
\r
6054 int fromX, fromY, toX, toY;
\r
6057 switch (message) {
\r
6058 case WM_INITDIALOG:
\r
6059 move[0] = (char) lParam;
\r
6060 move[1] = NULLCHAR;
\r
6061 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6062 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6063 SetWindowText(hInput, move);
\r
6065 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6069 switch (LOWORD(wParam)) {
\r
6071 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6072 { int n; Board board;
\r
6074 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6075 EditPositionPasteFEN(move);
\r
6076 EndDialog(hDlg, TRUE);
\r
6079 // [HGM] movenum: allow move number to be typed in any mode
\r
6080 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6082 EndDialog(hDlg, TRUE);
\r
6086 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6087 gameMode != Training) {
\r
6088 DisplayMoveError("Displayed move is not current");
\r
6090 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6091 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6092 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6093 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6094 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6095 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6096 if (gameMode != Training)
\r
6097 forwardMostMove = currentMove;
\r
6098 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6100 DisplayMoveError("Could not parse move");
\r
6103 EndDialog(hDlg, TRUE);
\r
6106 EndDialog(hDlg, FALSE);
\r
6117 PopUpMoveDialog(char firstchar)
\r
6121 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6122 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6123 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6124 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6125 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6126 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6127 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6128 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6129 gameMode == Training) {
\r
6130 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6131 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6132 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6133 FreeProcInstance(lpProc);
\r
6137 /*---------------------------------------------------------------------------*\
\r
6139 * Type-in name dialog functions
\r
6141 \*---------------------------------------------------------------------------*/
\r
6144 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6146 char move[MSG_SIZ];
\r
6149 switch (message) {
\r
6150 case WM_INITDIALOG:
\r
6151 move[0] = (char) lParam;
\r
6152 move[1] = NULLCHAR;
\r
6153 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6154 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6155 SetWindowText(hInput, move);
\r
6157 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6161 switch (LOWORD(wParam)) {
\r
6163 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6164 appData.userName = strdup(move);
\r
6167 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6168 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6169 DisplayTitle(move);
\r
6173 EndDialog(hDlg, TRUE);
\r
6176 EndDialog(hDlg, FALSE);
\r
6187 PopUpNameDialog(char firstchar)
\r
6191 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6192 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6193 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6194 FreeProcInstance(lpProc);
\r
6197 /*---------------------------------------------------------------------------*\
\r
6201 \*---------------------------------------------------------------------------*/
\r
6203 /* Nonmodal error box */
\r
6204 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6205 WPARAM wParam, LPARAM lParam);
\r
6208 ErrorPopUp(char *title, char *content)
\r
6212 BOOLEAN modal = hwndMain == NULL;
\r
6230 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6231 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6234 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6236 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6237 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6238 hwndMain, (DLGPROC)lpProc);
\r
6239 FreeProcInstance(lpProc);
\r
6246 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6247 if (errorDialog == NULL) return;
\r
6248 DestroyWindow(errorDialog);
\r
6249 errorDialog = NULL;
\r
6250 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6254 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6259 switch (message) {
\r
6260 case WM_INITDIALOG:
\r
6261 GetWindowRect(hDlg, &rChild);
\r
6264 SetWindowPos(hDlg, NULL, rChild.left,
\r
6265 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6266 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6270 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6271 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6272 and it doesn't work when you resize the dialog.
\r
6273 For now, just give it a default position.
\r
6275 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6277 errorDialog = hDlg;
\r
6278 SetWindowText(hDlg, errorTitle);
\r
6279 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6280 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6284 switch (LOWORD(wParam)) {
\r
6287 if (errorDialog == hDlg) errorDialog = NULL;
\r
6288 DestroyWindow(hDlg);
\r
6300 HWND gothicDialog = NULL;
\r
6303 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6307 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6309 switch (message) {
\r
6310 case WM_INITDIALOG:
\r
6311 GetWindowRect(hDlg, &rChild);
\r
6313 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6317 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6318 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6319 and it doesn't work when you resize the dialog.
\r
6320 For now, just give it a default position.
\r
6322 gothicDialog = hDlg;
\r
6323 SetWindowText(hDlg, errorTitle);
\r
6324 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6325 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6329 switch (LOWORD(wParam)) {
\r
6332 if (errorDialog == hDlg) errorDialog = NULL;
\r
6333 DestroyWindow(hDlg);
\r
6345 GothicPopUp(char *title, VariantClass variant)
\r
6348 static char *lastTitle;
\r
6350 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6351 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6353 if(lastTitle != title && gothicDialog != NULL) {
\r
6354 DestroyWindow(gothicDialog);
\r
6355 gothicDialog = NULL;
\r
6357 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6358 title = lastTitle;
\r
6359 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6360 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6361 hwndMain, (DLGPROC)lpProc);
\r
6362 FreeProcInstance(lpProc);
\r
6367 /*---------------------------------------------------------------------------*\
\r
6369 * Ics Interaction console functions
\r
6371 \*---------------------------------------------------------------------------*/
\r
6373 #define HISTORY_SIZE 64
\r
6374 static char *history[HISTORY_SIZE];
\r
6375 int histIn = 0, histP = 0;
\r
6378 SaveInHistory(char *cmd)
\r
6380 if (history[histIn] != NULL) {
\r
6381 free(history[histIn]);
\r
6382 history[histIn] = NULL;
\r
6384 if (*cmd == NULLCHAR) return;
\r
6385 history[histIn] = StrSave(cmd);
\r
6386 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6387 if (history[histIn] != NULL) {
\r
6388 free(history[histIn]);
\r
6389 history[histIn] = NULL;
\r
6395 PrevInHistory(char *cmd)
\r
6398 if (histP == histIn) {
\r
6399 if (history[histIn] != NULL) free(history[histIn]);
\r
6400 history[histIn] = StrSave(cmd);
\r
6402 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6403 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6405 return history[histP];
\r
6411 if (histP == histIn) return NULL;
\r
6412 histP = (histP + 1) % HISTORY_SIZE;
\r
6413 return history[histP];
\r
6417 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6421 hmenu = LoadMenu(hInst, "TextMenu");
\r
6422 h = GetSubMenu(hmenu, 0);
\r
6424 if (strcmp(e->item, "-") == 0) {
\r
6425 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6426 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6427 int flags = MF_STRING, j = 0;
\r
6428 if (e->item[0] == '|') {
\r
6429 flags |= MF_MENUBARBREAK;
\r
6432 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6433 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6441 WNDPROC consoleTextWindowProc;
\r
6444 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6446 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6447 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6451 SetWindowText(hInput, command);
\r
6453 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6455 sel.cpMin = 999999;
\r
6456 sel.cpMax = 999999;
\r
6457 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6462 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6463 if (sel.cpMin == sel.cpMax) {
\r
6464 /* Expand to surrounding word */
\r
6467 tr.chrg.cpMax = sel.cpMin;
\r
6468 tr.chrg.cpMin = --sel.cpMin;
\r
6469 if (sel.cpMin < 0) break;
\r
6470 tr.lpstrText = name;
\r
6471 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6472 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6476 tr.chrg.cpMin = sel.cpMax;
\r
6477 tr.chrg.cpMax = ++sel.cpMax;
\r
6478 tr.lpstrText = name;
\r
6479 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6480 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6483 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6484 MessageBeep(MB_ICONEXCLAMATION);
\r
6488 tr.lpstrText = name;
\r
6489 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6491 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6492 MessageBeep(MB_ICONEXCLAMATION);
\r
6495 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6498 sprintf(buf, "%s %s", command, name);
\r
6499 SetWindowText(hInput, buf);
\r
6500 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6502 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6503 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6504 SetWindowText(hInput, buf);
\r
6505 sel.cpMin = 999999;
\r
6506 sel.cpMax = 999999;
\r
6507 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6513 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6518 switch (message) {
\r
6520 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6523 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6526 sel.cpMin = 999999;
\r
6527 sel.cpMax = 999999;
\r
6528 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6529 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6534 if(wParam != '\022') {
\r
6535 if (wParam == '\t') {
\r
6536 if (GetKeyState(VK_SHIFT) < 0) {
\r
6538 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6539 if (buttonDesc[0].hwnd) {
\r
6540 SetFocus(buttonDesc[0].hwnd);
\r
6542 SetFocus(hwndMain);
\r
6546 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6549 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6550 JAWS_DELETE( SetFocus(hInput); )
\r
6551 SendMessage(hInput, message, wParam, lParam);
\r
6554 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6555 case WM_RBUTTONDOWN:
\r
6556 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6557 /* Move selection here if it was empty */
\r
6559 pt.x = LOWORD(lParam);
\r
6560 pt.y = HIWORD(lParam);
\r
6561 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6562 if (sel.cpMin == sel.cpMax) {
\r
6563 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6564 sel.cpMax = sel.cpMin;
\r
6565 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6567 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6568 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6570 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6571 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6572 if (sel.cpMin == sel.cpMax) {
\r
6573 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6574 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6576 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6577 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6579 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6580 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6581 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6582 MenuPopup(hwnd, pt, hmenu, -1);
\r
6586 case WM_RBUTTONUP:
\r
6587 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6588 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6589 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6593 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6595 return SendMessage(hInput, message, wParam, lParam);
\r
6596 case WM_MBUTTONDOWN:
\r
6597 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6599 switch (LOWORD(wParam)) {
\r
6600 case IDM_QuickPaste:
\r
6602 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6603 if (sel.cpMin == sel.cpMax) {
\r
6604 MessageBeep(MB_ICONEXCLAMATION);
\r
6607 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6608 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6609 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6614 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6617 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6620 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6624 int i = LOWORD(wParam) - IDM_CommandX;
\r
6625 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6626 icsTextMenuEntry[i].command != NULL) {
\r
6627 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6628 icsTextMenuEntry[i].getname,
\r
6629 icsTextMenuEntry[i].immediate);
\r
6637 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6640 WNDPROC consoleInputWindowProc;
\r
6643 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6645 char buf[MSG_SIZ];
\r
6647 static BOOL sendNextChar = FALSE;
\r
6648 static BOOL quoteNextChar = FALSE;
\r
6649 InputSource *is = consoleInputSource;
\r
6653 switch (message) {
\r
6655 if (!appData.localLineEditing || sendNextChar) {
\r
6656 is->buf[0] = (CHAR) wParam;
\r
6658 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6659 sendNextChar = FALSE;
\r
6662 if (quoteNextChar) {
\r
6663 buf[0] = (char) wParam;
\r
6664 buf[1] = NULLCHAR;
\r
6665 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6666 quoteNextChar = FALSE;
\r
6670 case '\r': /* Enter key */
\r
6671 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6672 if (consoleEcho) SaveInHistory(is->buf);
\r
6673 is->buf[is->count++] = '\n';
\r
6674 is->buf[is->count] = NULLCHAR;
\r
6675 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6676 if (consoleEcho) {
\r
6677 ConsoleOutput(is->buf, is->count, TRUE);
\r
6678 } else if (appData.localLineEditing) {
\r
6679 ConsoleOutput("\n", 1, TRUE);
\r
6682 case '\033': /* Escape key */
\r
6683 SetWindowText(hwnd, "");
\r
6684 cf.cbSize = sizeof(CHARFORMAT);
\r
6685 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6686 if (consoleEcho) {
\r
6687 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6689 cf.crTextColor = COLOR_ECHOOFF;
\r
6691 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6692 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6694 case '\t': /* Tab key */
\r
6695 if (GetKeyState(VK_SHIFT) < 0) {
\r
6697 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6700 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6701 if (buttonDesc[0].hwnd) {
\r
6702 SetFocus(buttonDesc[0].hwnd);
\r
6704 SetFocus(hwndMain);
\r
6708 case '\023': /* Ctrl+S */
\r
6709 sendNextChar = TRUE;
\r
6711 case '\021': /* Ctrl+Q */
\r
6712 quoteNextChar = TRUE;
\r
6722 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6723 p = PrevInHistory(buf);
\r
6725 SetWindowText(hwnd, p);
\r
6726 sel.cpMin = 999999;
\r
6727 sel.cpMax = 999999;
\r
6728 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6733 p = NextInHistory();
\r
6735 SetWindowText(hwnd, p);
\r
6736 sel.cpMin = 999999;
\r
6737 sel.cpMax = 999999;
\r
6738 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6744 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6748 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6752 case WM_MBUTTONDOWN:
\r
6753 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6754 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6756 case WM_RBUTTONUP:
\r
6757 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6758 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6759 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6763 hmenu = LoadMenu(hInst, "InputMenu");
\r
6764 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6765 if (sel.cpMin == sel.cpMax) {
\r
6766 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6767 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6769 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6770 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6772 pt.x = LOWORD(lParam);
\r
6773 pt.y = HIWORD(lParam);
\r
6774 MenuPopup(hwnd, pt, hmenu, -1);
\r
6778 switch (LOWORD(wParam)) {
\r
6780 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6782 case IDM_SelectAll:
\r
6784 sel.cpMax = -1; /*999999?*/
\r
6785 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6788 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6791 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6794 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6799 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6802 #define CO_MAX 100000
\r
6803 #define CO_TRIM 1000
\r
6806 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6808 static SnapData sd;
\r
6809 HWND hText, hInput;
\r
6811 static int sizeX, sizeY;
\r
6812 int newSizeX, newSizeY;
\r
6816 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6817 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6819 switch (message) {
\r
6821 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6823 ENLINK *pLink = (ENLINK*)lParam;
\r
6824 if (pLink->msg == WM_LBUTTONUP)
\r
6828 tr.chrg = pLink->chrg;
\r
6829 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6830 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6831 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6832 free(tr.lpstrText);
\r
6836 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6837 hwndConsole = hDlg;
\r
6839 consoleTextWindowProc = (WNDPROC)
\r
6840 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6841 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6842 consoleInputWindowProc = (WNDPROC)
\r
6843 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6844 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6845 Colorize(ColorNormal, TRUE);
\r
6846 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6847 ChangedConsoleFont();
\r
6848 GetClientRect(hDlg, &rect);
\r
6849 sizeX = rect.right;
\r
6850 sizeY = rect.bottom;
\r
6851 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6852 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6853 WINDOWPLACEMENT wp;
\r
6854 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6855 wp.length = sizeof(WINDOWPLACEMENT);
\r
6857 wp.showCmd = SW_SHOW;
\r
6858 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6859 wp.rcNormalPosition.left = wpConsole.x;
\r
6860 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6861 wp.rcNormalPosition.top = wpConsole.y;
\r
6862 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6863 SetWindowPlacement(hDlg, &wp);
\r
6866 // [HGM] Chessknight's change 2004-07-13
\r
6867 else { /* Determine Defaults */
\r
6868 WINDOWPLACEMENT wp;
\r
6869 wpConsole.x = wpMain.width + 1;
\r
6870 wpConsole.y = wpMain.y;
\r
6871 wpConsole.width = screenWidth - wpMain.width;
\r
6872 wpConsole.height = wpMain.height;
\r
6873 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6874 wp.length = sizeof(WINDOWPLACEMENT);
\r
6876 wp.showCmd = SW_SHOW;
\r
6877 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6878 wp.rcNormalPosition.left = wpConsole.x;
\r
6879 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6880 wp.rcNormalPosition.top = wpConsole.y;
\r
6881 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6882 SetWindowPlacement(hDlg, &wp);
\r
6885 // Allow hText to highlight URLs and send notifications on them
\r
6886 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6887 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6888 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6889 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6903 if (IsIconic(hDlg)) break;
\r
6904 newSizeX = LOWORD(lParam);
\r
6905 newSizeY = HIWORD(lParam);
\r
6906 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6907 RECT rectText, rectInput;
\r
6909 int newTextHeight, newTextWidth;
\r
6910 GetWindowRect(hText, &rectText);
\r
6911 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6912 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6913 if (newTextHeight < 0) {
\r
6914 newSizeY += -newTextHeight;
\r
6915 newTextHeight = 0;
\r
6917 SetWindowPos(hText, NULL, 0, 0,
\r
6918 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6919 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6920 pt.x = rectInput.left;
\r
6921 pt.y = rectInput.top + newSizeY - sizeY;
\r
6922 ScreenToClient(hDlg, &pt);
\r
6923 SetWindowPos(hInput, NULL,
\r
6924 pt.x, pt.y, /* needs client coords */
\r
6925 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6926 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6932 case WM_GETMINMAXINFO:
\r
6933 /* Prevent resizing window too small */
\r
6934 mmi = (MINMAXINFO *) lParam;
\r
6935 mmi->ptMinTrackSize.x = 100;
\r
6936 mmi->ptMinTrackSize.y = 100;
\r
6939 /* [AS] Snapping */
\r
6940 case WM_ENTERSIZEMOVE:
\r
6941 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6944 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6947 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6949 case WM_EXITSIZEMOVE:
\r
6950 UpdateICSWidth(hText);
\r
6951 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6954 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6962 if (hwndConsole) return;
\r
6963 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6964 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6969 ConsoleOutput(char* data, int length, int forceVisible)
\r
6974 char buf[CO_MAX+1];
\r
6977 static int delayLF = 0;
\r
6978 CHARRANGE savesel, sel;
\r
6980 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6988 while (length--) {
\r
6996 } else if (*p == '\007') {
\r
6997 MyPlaySound(&sounds[(int)SoundBell]);
\r
7004 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7005 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7006 /* Save current selection */
\r
7007 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7008 exlen = GetWindowTextLength(hText);
\r
7009 /* Find out whether current end of text is visible */
\r
7010 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7011 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7012 /* Trim existing text if it's too long */
\r
7013 if (exlen + (q - buf) > CO_MAX) {
\r
7014 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7017 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7018 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7020 savesel.cpMin -= trim;
\r
7021 savesel.cpMax -= trim;
\r
7022 if (exlen < 0) exlen = 0;
\r
7023 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7024 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7026 /* Append the new text */
\r
7027 sel.cpMin = exlen;
\r
7028 sel.cpMax = exlen;
\r
7029 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7030 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7031 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7032 if (forceVisible || exlen == 0 ||
\r
7033 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7034 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7035 /* Scroll to make new end of text visible if old end of text
\r
7036 was visible or new text is an echo of user typein */
\r
7037 sel.cpMin = 9999999;
\r
7038 sel.cpMax = 9999999;
\r
7039 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7040 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7041 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7042 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7044 if (savesel.cpMax == exlen || forceVisible) {
\r
7045 /* Move insert point to new end of text if it was at the old
\r
7046 end of text or if the new text is an echo of user typein */
\r
7047 sel.cpMin = 9999999;
\r
7048 sel.cpMax = 9999999;
\r
7049 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7051 /* Restore previous selection */
\r
7052 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7054 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7061 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7065 COLORREF oldFg, oldBg;
\r
7069 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7071 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7072 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7073 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7076 rect.right = x + squareSize;
\r
7078 rect.bottom = y + squareSize;
\r
7081 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7082 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7083 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7084 &rect, str, strlen(str), NULL);
\r
7086 (void) SetTextColor(hdc, oldFg);
\r
7087 (void) SetBkColor(hdc, oldBg);
\r
7088 (void) SelectObject(hdc, oldFont);
\r
7092 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7093 RECT *rect, char *color, char *flagFell)
\r
7097 COLORREF oldFg, oldBg;
\r
7100 if (appData.clockMode) {
\r
7102 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7104 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7111 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7112 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7114 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7115 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7117 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7121 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7122 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7123 rect, str, strlen(str), NULL);
\r
7124 if(logoHeight > 0 && appData.clockMode) {
\r
7126 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7127 r.top = rect->top + logoHeight/2;
\r
7128 r.left = rect->left;
\r
7129 r.right = rect->right;
\r
7130 r.bottom = rect->bottom;
\r
7131 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7132 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7133 &r, str, strlen(str), NULL);
\r
7135 (void) SetTextColor(hdc, oldFg);
\r
7136 (void) SetBkColor(hdc, oldBg);
\r
7137 (void) SelectObject(hdc, oldFont);
\r
7142 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7148 if( count <= 0 ) {
\r
7149 if (appData.debugMode) {
\r
7150 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7153 return ERROR_INVALID_USER_BUFFER;
\r
7156 ResetEvent(ovl->hEvent);
\r
7157 ovl->Offset = ovl->OffsetHigh = 0;
\r
7158 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7162 err = GetLastError();
\r
7163 if (err == ERROR_IO_PENDING) {
\r
7164 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7168 err = GetLastError();
\r
7175 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7180 ResetEvent(ovl->hEvent);
\r
7181 ovl->Offset = ovl->OffsetHigh = 0;
\r
7182 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7186 err = GetLastError();
\r
7187 if (err == ERROR_IO_PENDING) {
\r
7188 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7192 err = GetLastError();
\r
7198 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7199 void CheckForInputBufferFull( InputSource * is )
\r
7201 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7202 /* Look for end of line */
\r
7203 char * p = is->buf;
\r
7205 while( p < is->next && *p != '\n' ) {
\r
7209 if( p >= is->next ) {
\r
7210 if (appData.debugMode) {
\r
7211 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7214 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7215 is->count = (DWORD) -1;
\r
7216 is->next = is->buf;
\r
7222 InputThread(LPVOID arg)
\r
7227 is = (InputSource *) arg;
\r
7228 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7229 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7230 while (is->hThread != NULL) {
\r
7231 is->error = DoReadFile(is->hFile, is->next,
\r
7232 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7233 &is->count, &ovl);
\r
7234 if (is->error == NO_ERROR) {
\r
7235 is->next += is->count;
\r
7237 if (is->error == ERROR_BROKEN_PIPE) {
\r
7238 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7241 is->count = (DWORD) -1;
\r
7242 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7247 CheckForInputBufferFull( is );
\r
7249 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7251 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7253 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7256 CloseHandle(ovl.hEvent);
\r
7257 CloseHandle(is->hFile);
\r
7259 if (appData.debugMode) {
\r
7260 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7267 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7269 NonOvlInputThread(LPVOID arg)
\r
7276 is = (InputSource *) arg;
\r
7277 while (is->hThread != NULL) {
\r
7278 is->error = ReadFile(is->hFile, is->next,
\r
7279 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7280 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7281 if (is->error == NO_ERROR) {
\r
7282 /* Change CRLF to LF */
\r
7283 if (is->next > is->buf) {
\r
7285 i = is->count + 1;
\r
7293 if (prev == '\r' && *p == '\n') {
\r
7305 if (is->error == ERROR_BROKEN_PIPE) {
\r
7306 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7309 is->count = (DWORD) -1;
\r
7313 CheckForInputBufferFull( is );
\r
7315 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7317 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7319 if (is->count < 0) break; /* Quit on error */
\r
7321 CloseHandle(is->hFile);
\r
7326 SocketInputThread(LPVOID arg)
\r
7330 is = (InputSource *) arg;
\r
7331 while (is->hThread != NULL) {
\r
7332 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7333 if ((int)is->count == SOCKET_ERROR) {
\r
7334 is->count = (DWORD) -1;
\r
7335 is->error = WSAGetLastError();
\r
7337 is->error = NO_ERROR;
\r
7338 is->next += is->count;
\r
7339 if (is->count == 0 && is->second == is) {
\r
7340 /* End of file on stderr; quit with no message */
\r
7344 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7346 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7348 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7354 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7358 is = (InputSource *) lParam;
\r
7359 if (is->lineByLine) {
\r
7360 /* Feed in lines one by one */
\r
7361 char *p = is->buf;
\r
7363 while (q < is->next) {
\r
7364 if (*q++ == '\n') {
\r
7365 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7370 /* Move any partial line to the start of the buffer */
\r
7372 while (p < is->next) {
\r
7377 if (is->error != NO_ERROR || is->count == 0) {
\r
7378 /* Notify backend of the error. Note: If there was a partial
\r
7379 line at the end, it is not flushed through. */
\r
7380 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7383 /* Feed in the whole chunk of input at once */
\r
7384 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7385 is->next = is->buf;
\r
7389 /*---------------------------------------------------------------------------*\
\r
7391 * Menu enables. Used when setting various modes.
\r
7393 \*---------------------------------------------------------------------------*/
\r
7401 GreyRevert(Boolean grey)
\r
7402 { // [HGM] vari: for retracting variations in local mode
\r
7403 HMENU hmenu = GetMenu(hwndMain);
\r
7404 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7405 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7409 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7411 while (enab->item > 0) {
\r
7412 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7417 Enables gnuEnables[] = {
\r
7418 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7419 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7420 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7421 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7422 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7424 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7425 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7426 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7427 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7428 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7429 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7434 Enables icsEnables[] = {
\r
7435 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7437 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7439 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7440 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7441 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7442 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7443 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7444 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7445 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7446 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7447 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7448 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7449 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7450 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7455 Enables zippyEnables[] = {
\r
7456 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7457 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7458 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7459 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7464 Enables ncpEnables[] = {
\r
7465 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7466 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7467 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7471 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7472 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7473 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7474 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7475 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7476 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7477 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7479 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7480 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7481 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7482 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7483 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7484 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7488 Enables trainingOnEnables[] = {
\r
7489 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7490 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7491 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7492 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7493 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7494 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7495 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7496 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7500 Enables trainingOffEnables[] = {
\r
7501 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7502 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7503 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7504 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7505 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7506 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7507 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7508 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7512 /* These modify either ncpEnables or gnuEnables */
\r
7513 Enables cmailEnables[] = {
\r
7514 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7515 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7516 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7517 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7518 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7519 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7520 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7524 Enables machineThinkingEnables[] = {
\r
7525 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7526 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7527 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7528 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7529 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7530 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7531 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7532 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7533 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7534 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7535 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7536 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7537 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7538 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7539 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7543 Enables userThinkingEnables[] = {
\r
7544 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7545 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7546 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7547 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7548 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7549 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7550 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7551 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7552 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7553 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7554 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7555 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7556 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7557 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7558 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7562 /*---------------------------------------------------------------------------*\
\r
7564 * Front-end interface functions exported by XBoard.
\r
7565 * Functions appear in same order as prototypes in frontend.h.
\r
7567 \*---------------------------------------------------------------------------*/
\r
7571 static UINT prevChecked = 0;
\r
7572 static int prevPausing = 0;
\r
7575 if (pausing != prevPausing) {
\r
7576 prevPausing = pausing;
\r
7577 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7578 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7579 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7582 switch (gameMode) {
\r
7583 case BeginningOfGame:
\r
7584 if (appData.icsActive)
\r
7585 nowChecked = IDM_IcsClient;
\r
7586 else if (appData.noChessProgram)
\r
7587 nowChecked = IDM_EditGame;
\r
7589 nowChecked = IDM_MachineBlack;
\r
7591 case MachinePlaysBlack:
\r
7592 nowChecked = IDM_MachineBlack;
\r
7594 case MachinePlaysWhite:
\r
7595 nowChecked = IDM_MachineWhite;
\r
7597 case TwoMachinesPlay:
\r
7598 nowChecked = IDM_TwoMachines;
\r
7601 nowChecked = IDM_AnalysisMode;
\r
7604 nowChecked = IDM_AnalyzeFile;
\r
7607 nowChecked = IDM_EditGame;
\r
7609 case PlayFromGameFile:
\r
7610 nowChecked = IDM_LoadGame;
\r
7612 case EditPosition:
\r
7613 nowChecked = IDM_EditPosition;
\r
7616 nowChecked = IDM_Training;
\r
7618 case IcsPlayingWhite:
\r
7619 case IcsPlayingBlack:
\r
7620 case IcsObserving:
\r
7622 nowChecked = IDM_IcsClient;
\r
7629 if (prevChecked != 0)
\r
7630 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7631 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7632 if (nowChecked != 0)
\r
7633 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7634 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7636 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7637 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7638 MF_BYCOMMAND|MF_ENABLED);
\r
7640 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7641 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7644 prevChecked = nowChecked;
\r
7646 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7647 if (appData.icsActive) {
\r
7648 if (appData.icsEngineAnalyze) {
\r
7649 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7650 MF_BYCOMMAND|MF_CHECKED);
\r
7652 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7653 MF_BYCOMMAND|MF_UNCHECKED);
\r
7661 HMENU hmenu = GetMenu(hwndMain);
\r
7662 SetMenuEnables(hmenu, icsEnables);
\r
7663 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7664 MF_BYPOSITION|MF_ENABLED);
\r
7666 if (appData.zippyPlay) {
\r
7667 SetMenuEnables(hmenu, zippyEnables);
\r
7668 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7669 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7670 MF_BYCOMMAND|MF_ENABLED);
\r
7678 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7684 HMENU hmenu = GetMenu(hwndMain);
\r
7685 SetMenuEnables(hmenu, ncpEnables);
\r
7686 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7687 MF_BYPOSITION|MF_GRAYED);
\r
7688 DrawMenuBar(hwndMain);
\r
7694 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7698 SetTrainingModeOn()
\r
7701 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7702 for (i = 0; i < N_BUTTONS; i++) {
\r
7703 if (buttonDesc[i].hwnd != NULL)
\r
7704 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7709 VOID SetTrainingModeOff()
\r
7712 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7713 for (i = 0; i < N_BUTTONS; i++) {
\r
7714 if (buttonDesc[i].hwnd != NULL)
\r
7715 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7721 SetUserThinkingEnables()
\r
7723 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7727 SetMachineThinkingEnables()
\r
7729 HMENU hMenu = GetMenu(hwndMain);
\r
7730 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7732 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7734 if (gameMode == MachinePlaysBlack) {
\r
7735 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7736 } else if (gameMode == MachinePlaysWhite) {
\r
7737 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7738 } else if (gameMode == TwoMachinesPlay) {
\r
7739 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7745 DisplayTitle(char *str)
\r
7747 char title[MSG_SIZ], *host;
\r
7748 if (str[0] != NULLCHAR) {
\r
7749 strcpy(title, str);
\r
7750 } else if (appData.icsActive) {
\r
7751 if (appData.icsCommPort[0] != NULLCHAR)
\r
7754 host = appData.icsHost;
\r
7755 sprintf(title, "%s: %s", szTitle, host);
\r
7756 } else if (appData.noChessProgram) {
\r
7757 strcpy(title, szTitle);
\r
7759 strcpy(title, szTitle);
\r
7760 strcat(title, ": ");
\r
7761 strcat(title, first.tidy);
\r
7763 SetWindowText(hwndMain, title);
\r
7768 DisplayMessage(char *str1, char *str2)
\r
7772 int remain = MESSAGE_TEXT_MAX - 1;
\r
7775 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7776 messageText[0] = NULLCHAR;
\r
7778 len = strlen(str1);
\r
7779 if (len > remain) len = remain;
\r
7780 strncpy(messageText, str1, len);
\r
7781 messageText[len] = NULLCHAR;
\r
7784 if (*str2 && remain >= 2) {
\r
7786 strcat(messageText, " ");
\r
7789 len = strlen(str2);
\r
7790 if (len > remain) len = remain;
\r
7791 strncat(messageText, str2, len);
\r
7793 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7795 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7799 hdc = GetDC(hwndMain);
\r
7800 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7801 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7802 &messageRect, messageText, strlen(messageText), NULL);
\r
7803 (void) SelectObject(hdc, oldFont);
\r
7804 (void) ReleaseDC(hwndMain, hdc);
\r
7808 DisplayError(char *str, int error)
\r
7810 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7816 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7817 NULL, error, LANG_NEUTRAL,
\r
7818 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7820 sprintf(buf, "%s:\n%s", str, buf2);
\r
7822 ErrorMap *em = errmap;
\r
7823 while (em->err != 0 && em->err != error) em++;
\r
7824 if (em->err != 0) {
\r
7825 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7827 sprintf(buf, "%s:\nError code %d", str, error);
\r
7832 ErrorPopUp("Error", buf);
\r
7837 DisplayMoveError(char *str)
\r
7839 fromX = fromY = -1;
\r
7840 ClearHighlights();
\r
7841 DrawPosition(FALSE, NULL);
\r
7842 if (appData.popupMoveErrors) {
\r
7843 ErrorPopUp("Error", str);
\r
7845 DisplayMessage(str, "");
\r
7846 moveErrorMessageUp = TRUE;
\r
7851 DisplayFatalError(char *str, int error, int exitStatus)
\r
7853 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7855 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7858 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7859 NULL, error, LANG_NEUTRAL,
\r
7860 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7862 sprintf(buf, "%s:\n%s", str, buf2);
\r
7864 ErrorMap *em = errmap;
\r
7865 while (em->err != 0 && em->err != error) em++;
\r
7866 if (em->err != 0) {
\r
7867 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7869 sprintf(buf, "%s:\nError code %d", str, error);
\r
7874 if (appData.debugMode) {
\r
7875 fprintf(debugFP, "%s: %s\n", label, str);
\r
7877 if (appData.popupExitMessage) {
\r
7878 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7879 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7881 ExitEvent(exitStatus);
\r
7886 DisplayInformation(char *str)
\r
7888 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7893 DisplayNote(char *str)
\r
7895 ErrorPopUp("Note", str);
\r
7900 char *title, *question, *replyPrefix;
\r
7905 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7907 static QuestionParams *qp;
\r
7908 char reply[MSG_SIZ];
\r
7911 switch (message) {
\r
7912 case WM_INITDIALOG:
\r
7913 qp = (QuestionParams *) lParam;
\r
7914 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7915 SetWindowText(hDlg, qp->title);
\r
7916 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7917 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7921 switch (LOWORD(wParam)) {
\r
7923 strcpy(reply, qp->replyPrefix);
\r
7924 if (*reply) strcat(reply, " ");
\r
7925 len = strlen(reply);
\r
7926 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7927 strcat(reply, "\n");
\r
7928 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7929 EndDialog(hDlg, TRUE);
\r
7930 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7933 EndDialog(hDlg, FALSE);
\r
7944 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7946 QuestionParams qp;
\r
7950 qp.question = question;
\r
7951 qp.replyPrefix = replyPrefix;
\r
7953 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7954 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7955 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7956 FreeProcInstance(lpProc);
\r
7959 /* [AS] Pick FRC position */
\r
7960 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7962 static int * lpIndexFRC;
\r
7968 case WM_INITDIALOG:
\r
7969 lpIndexFRC = (int *) lParam;
\r
7971 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7973 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7974 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7975 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7976 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7981 switch( LOWORD(wParam) ) {
\r
7983 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7984 EndDialog( hDlg, 0 );
\r
7985 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7988 EndDialog( hDlg, 1 );
\r
7990 case IDC_NFG_Edit:
\r
7991 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7992 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7994 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7997 case IDC_NFG_Random:
\r
7998 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7999 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8012 int index = appData.defaultFrcPosition;
\r
8013 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8015 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8017 if( result == 0 ) {
\r
8018 appData.defaultFrcPosition = index;
\r
8024 /* [AS] Game list options. Refactored by HGM */
\r
8026 HWND gameListOptionsDialog;
\r
8028 // low-level front-end: clear text edit / list widget
\r
8032 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8035 // low-level front-end: clear text edit / list widget
\r
8037 GLT_DeSelectList()
\r
8039 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8042 // low-level front-end: append line to text edit / list widget
\r
8044 GLT_AddToList( char *name )
\r
8047 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8051 // low-level front-end: get line from text edit / list widget
\r
8053 GLT_GetFromList( int index, char *name )
\r
8056 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8062 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8064 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8065 int idx2 = idx1 + delta;
\r
8066 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8068 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8071 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8072 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8073 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8074 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8078 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8082 case WM_INITDIALOG:
\r
8083 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8085 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8087 /* Initialize list */
\r
8088 GLT_TagsToList( lpUserGLT );
\r
8090 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8095 switch( LOWORD(wParam) ) {
\r
8098 EndDialog( hDlg, 0 );
\r
8101 EndDialog( hDlg, 1 );
\r
8104 case IDC_GLT_Default:
\r
8105 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8108 case IDC_GLT_Restore:
\r
8109 GLT_TagsToList( appData.gameListTags );
\r
8113 GLT_MoveSelection( hDlg, -1 );
\r
8116 case IDC_GLT_Down:
\r
8117 GLT_MoveSelection( hDlg, +1 );
\r
8127 int GameListOptions()
\r
8130 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8132 strcpy( lpUserGLT, appData.gameListTags );
\r
8134 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8136 if( result == 0 ) {
\r
8137 /* [AS] Memory leak here! */
\r
8138 appData.gameListTags = strdup( lpUserGLT );
\r
8145 DisplayIcsInteractionTitle(char *str)
\r
8147 char consoleTitle[MSG_SIZ];
\r
8149 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8150 SetWindowText(hwndConsole, consoleTitle);
\r
8154 DrawPosition(int fullRedraw, Board board)
\r
8156 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8159 void NotifyFrontendLogin()
\r
8162 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8168 fromX = fromY = -1;
\r
8169 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8170 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8171 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8172 dragInfo.lastpos = dragInfo.pos;
\r
8173 dragInfo.start.x = dragInfo.start.y = -1;
\r
8174 dragInfo.from = dragInfo.start;
\r
8176 DrawPosition(TRUE, NULL);
\r
8183 CommentPopUp(char *title, char *str)
\r
8185 HWND hwnd = GetActiveWindow();
\r
8186 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8188 SetActiveWindow(hwnd);
\r
8192 CommentPopDown(void)
\r
8194 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8195 if (commentDialog) {
\r
8196 ShowWindow(commentDialog, SW_HIDE);
\r
8198 commentUp = FALSE;
\r
8202 EditCommentPopUp(int index, char *title, char *str)
\r
8204 EitherCommentPopUp(index, title, str, TRUE);
\r
8211 MyPlaySound(&sounds[(int)SoundMove]);
\r
8214 VOID PlayIcsWinSound()
\r
8216 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8219 VOID PlayIcsLossSound()
\r
8221 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8224 VOID PlayIcsDrawSound()
\r
8226 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8229 VOID PlayIcsUnfinishedSound()
\r
8231 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8237 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8245 consoleEcho = TRUE;
\r
8246 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8247 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8248 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8257 consoleEcho = FALSE;
\r
8258 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8259 /* This works OK: set text and background both to the same color */
\r
8261 cf.crTextColor = COLOR_ECHOOFF;
\r
8262 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8263 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8266 /* No Raw()...? */
\r
8268 void Colorize(ColorClass cc, int continuation)
\r
8270 currentColorClass = cc;
\r
8271 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8272 consoleCF.crTextColor = textAttribs[cc].color;
\r
8273 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8274 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8280 static char buf[MSG_SIZ];
\r
8281 DWORD bufsiz = MSG_SIZ;
\r
8283 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8284 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8286 if (!GetUserName(buf, &bufsiz)) {
\r
8287 /*DisplayError("Error getting user name", GetLastError());*/
\r
8288 strcpy(buf, "User");
\r
8296 static char buf[MSG_SIZ];
\r
8297 DWORD bufsiz = MSG_SIZ;
\r
8299 if (!GetComputerName(buf, &bufsiz)) {
\r
8300 /*DisplayError("Error getting host name", GetLastError());*/
\r
8301 strcpy(buf, "Unknown");
\r
8308 ClockTimerRunning()
\r
8310 return clockTimerEvent != 0;
\r
8316 if (clockTimerEvent == 0) return FALSE;
\r
8317 KillTimer(hwndMain, clockTimerEvent);
\r
8318 clockTimerEvent = 0;
\r
8323 StartClockTimer(long millisec)
\r
8325 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8326 (UINT) millisec, NULL);
\r
8330 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8333 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8335 if(appData.noGUI) return;
\r
8336 hdc = GetDC(hwndMain);
\r
8337 if (!IsIconic(hwndMain)) {
\r
8338 DisplayAClock(hdc, timeRemaining, highlight,
\r
8339 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8341 if (highlight && iconCurrent == iconBlack) {
\r
8342 iconCurrent = iconWhite;
\r
8343 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8344 if (IsIconic(hwndMain)) {
\r
8345 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8348 (void) ReleaseDC(hwndMain, hdc);
\r
8350 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8354 DisplayBlackClock(long timeRemaining, int highlight)
\r
8357 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8359 if(appData.noGUI) return;
\r
8360 hdc = GetDC(hwndMain);
\r
8361 if (!IsIconic(hwndMain)) {
\r
8362 DisplayAClock(hdc, timeRemaining, highlight,
\r
8363 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8365 if (highlight && iconCurrent == iconWhite) {
\r
8366 iconCurrent = iconBlack;
\r
8367 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8368 if (IsIconic(hwndMain)) {
\r
8369 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8372 (void) ReleaseDC(hwndMain, hdc);
\r
8374 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8379 LoadGameTimerRunning()
\r
8381 return loadGameTimerEvent != 0;
\r
8385 StopLoadGameTimer()
\r
8387 if (loadGameTimerEvent == 0) return FALSE;
\r
8388 KillTimer(hwndMain, loadGameTimerEvent);
\r
8389 loadGameTimerEvent = 0;
\r
8394 StartLoadGameTimer(long millisec)
\r
8396 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8397 (UINT) millisec, NULL);
\r
8405 char fileTitle[MSG_SIZ];
\r
8407 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8408 f = OpenFileDialog(hwndMain, "a", defName,
\r
8409 appData.oldSaveStyle ? "gam" : "pgn",
\r
8411 "Save Game to File", NULL, fileTitle, NULL);
\r
8413 SaveGame(f, 0, "");
\r
8420 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8422 if (delayedTimerEvent != 0) {
\r
8423 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8424 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8426 KillTimer(hwndMain, delayedTimerEvent);
\r
8427 delayedTimerEvent = 0;
\r
8428 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8429 delayedTimerCallback();
\r
8431 delayedTimerCallback = cb;
\r
8432 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8433 (UINT) millisec, NULL);
\r
8436 DelayedEventCallback
\r
8439 if (delayedTimerEvent) {
\r
8440 return delayedTimerCallback;
\r
8447 CancelDelayedEvent()
\r
8449 if (delayedTimerEvent) {
\r
8450 KillTimer(hwndMain, delayedTimerEvent);
\r
8451 delayedTimerEvent = 0;
\r
8455 DWORD GetWin32Priority(int nice)
\r
8456 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8458 REALTIME_PRIORITY_CLASS 0x00000100
\r
8459 HIGH_PRIORITY_CLASS 0x00000080
\r
8460 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8461 NORMAL_PRIORITY_CLASS 0x00000020
\r
8462 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8463 IDLE_PRIORITY_CLASS 0x00000040
\r
8465 if (nice < -15) return 0x00000080;
\r
8466 if (nice < 0) return 0x00008000;
\r
8467 if (nice == 0) return 0x00000020;
\r
8468 if (nice < 15) return 0x00004000;
\r
8469 return 0x00000040;
\r
8472 /* Start a child process running the given program.
\r
8473 The process's standard output can be read from "from", and its
\r
8474 standard input can be written to "to".
\r
8475 Exit with fatal error if anything goes wrong.
\r
8476 Returns an opaque pointer that can be used to destroy the process
\r
8480 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8482 #define BUFSIZE 4096
\r
8484 HANDLE hChildStdinRd, hChildStdinWr,
\r
8485 hChildStdoutRd, hChildStdoutWr;
\r
8486 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8487 SECURITY_ATTRIBUTES saAttr;
\r
8489 PROCESS_INFORMATION piProcInfo;
\r
8490 STARTUPINFO siStartInfo;
\r
8492 char buf[MSG_SIZ];
\r
8495 if (appData.debugMode) {
\r
8496 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8501 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8502 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8503 saAttr.bInheritHandle = TRUE;
\r
8504 saAttr.lpSecurityDescriptor = NULL;
\r
8507 * The steps for redirecting child's STDOUT:
\r
8508 * 1. Create anonymous pipe to be STDOUT for child.
\r
8509 * 2. Create a noninheritable duplicate of read handle,
\r
8510 * and close the inheritable read handle.
\r
8513 /* Create a pipe for the child's STDOUT. */
\r
8514 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8515 return GetLastError();
\r
8518 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8519 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8520 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8521 FALSE, /* not inherited */
\r
8522 DUPLICATE_SAME_ACCESS);
\r
8524 return GetLastError();
\r
8526 CloseHandle(hChildStdoutRd);
\r
8529 * The steps for redirecting child's STDIN:
\r
8530 * 1. Create anonymous pipe to be STDIN for child.
\r
8531 * 2. Create a noninheritable duplicate of write handle,
\r
8532 * and close the inheritable write handle.
\r
8535 /* Create a pipe for the child's STDIN. */
\r
8536 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8537 return GetLastError();
\r
8540 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8541 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8542 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8543 FALSE, /* not inherited */
\r
8544 DUPLICATE_SAME_ACCESS);
\r
8546 return GetLastError();
\r
8548 CloseHandle(hChildStdinWr);
\r
8550 /* Arrange to (1) look in dir for the child .exe file, and
\r
8551 * (2) have dir be the child's working directory. Interpret
\r
8552 * dir relative to the directory WinBoard loaded from. */
\r
8553 GetCurrentDirectory(MSG_SIZ, buf);
\r
8554 SetCurrentDirectory(installDir);
\r
8555 SetCurrentDirectory(dir);
\r
8557 /* Now create the child process. */
\r
8559 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8560 siStartInfo.lpReserved = NULL;
\r
8561 siStartInfo.lpDesktop = NULL;
\r
8562 siStartInfo.lpTitle = NULL;
\r
8563 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8564 siStartInfo.cbReserved2 = 0;
\r
8565 siStartInfo.lpReserved2 = NULL;
\r
8566 siStartInfo.hStdInput = hChildStdinRd;
\r
8567 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8568 siStartInfo.hStdError = hChildStdoutWr;
\r
8570 fSuccess = CreateProcess(NULL,
\r
8571 cmdLine, /* command line */
\r
8572 NULL, /* process security attributes */
\r
8573 NULL, /* primary thread security attrs */
\r
8574 TRUE, /* handles are inherited */
\r
8575 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8576 NULL, /* use parent's environment */
\r
8578 &siStartInfo, /* STARTUPINFO pointer */
\r
8579 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8581 err = GetLastError();
\r
8582 SetCurrentDirectory(buf); /* return to prev directory */
\r
8587 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8588 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8589 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8592 /* Close the handles we don't need in the parent */
\r
8593 CloseHandle(piProcInfo.hThread);
\r
8594 CloseHandle(hChildStdinRd);
\r
8595 CloseHandle(hChildStdoutWr);
\r
8597 /* Prepare return value */
\r
8598 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8599 cp->kind = CPReal;
\r
8600 cp->hProcess = piProcInfo.hProcess;
\r
8601 cp->pid = piProcInfo.dwProcessId;
\r
8602 cp->hFrom = hChildStdoutRdDup;
\r
8603 cp->hTo = hChildStdinWrDup;
\r
8605 *pr = (void *) cp;
\r
8607 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8608 2000 where engines sometimes don't see the initial command(s)
\r
8609 from WinBoard and hang. I don't understand how that can happen,
\r
8610 but the Sleep is harmless, so I've put it in. Others have also
\r
8611 reported what may be the same problem, so hopefully this will fix
\r
8612 it for them too. */
\r
8620 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8622 ChildProc *cp; int result;
\r
8624 cp = (ChildProc *) pr;
\r
8625 if (cp == NULL) return;
\r
8627 switch (cp->kind) {
\r
8629 /* TerminateProcess is considered harmful, so... */
\r
8630 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8631 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8632 /* The following doesn't work because the chess program
\r
8633 doesn't "have the same console" as WinBoard. Maybe
\r
8634 we could arrange for this even though neither WinBoard
\r
8635 nor the chess program uses a console for stdio? */
\r
8636 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8638 /* [AS] Special termination modes for misbehaving programs... */
\r
8639 if( signal == 9 ) {
\r
8640 result = TerminateProcess( cp->hProcess, 0 );
\r
8642 if ( appData.debugMode) {
\r
8643 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8646 else if( signal == 10 ) {
\r
8647 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8649 if( dw != WAIT_OBJECT_0 ) {
\r
8650 result = TerminateProcess( cp->hProcess, 0 );
\r
8652 if ( appData.debugMode) {
\r
8653 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8659 CloseHandle(cp->hProcess);
\r
8663 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8667 closesocket(cp->sock);
\r
8672 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8673 closesocket(cp->sock);
\r
8674 closesocket(cp->sock2);
\r
8682 InterruptChildProcess(ProcRef pr)
\r
8686 cp = (ChildProc *) pr;
\r
8687 if (cp == NULL) return;
\r
8688 switch (cp->kind) {
\r
8690 /* The following doesn't work because the chess program
\r
8691 doesn't "have the same console" as WinBoard. Maybe
\r
8692 we could arrange for this even though neither WinBoard
\r
8693 nor the chess program uses a console for stdio */
\r
8694 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8699 /* Can't interrupt */
\r
8703 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8710 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8712 char cmdLine[MSG_SIZ];
\r
8714 if (port[0] == NULLCHAR) {
\r
8715 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8717 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8719 return StartChildProcess(cmdLine, "", pr);
\r
8723 /* Code to open TCP sockets */
\r
8726 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8731 struct sockaddr_in sa, mysa;
\r
8732 struct hostent FAR *hp;
\r
8733 unsigned short uport;
\r
8734 WORD wVersionRequested;
\r
8737 /* Initialize socket DLL */
\r
8738 wVersionRequested = MAKEWORD(1, 1);
\r
8739 err = WSAStartup(wVersionRequested, &wsaData);
\r
8740 if (err != 0) return err;
\r
8743 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8744 err = WSAGetLastError();
\r
8749 /* Bind local address using (mostly) don't-care values.
\r
8751 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8752 mysa.sin_family = AF_INET;
\r
8753 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8754 uport = (unsigned short) 0;
\r
8755 mysa.sin_port = htons(uport);
\r
8756 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8757 == SOCKET_ERROR) {
\r
8758 err = WSAGetLastError();
\r
8763 /* Resolve remote host name */
\r
8764 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8765 if (!(hp = gethostbyname(host))) {
\r
8766 unsigned int b0, b1, b2, b3;
\r
8768 err = WSAGetLastError();
\r
8770 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8771 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8772 hp->h_addrtype = AF_INET;
\r
8774 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8775 hp->h_addr_list[0] = (char *) malloc(4);
\r
8776 hp->h_addr_list[0][0] = (char) b0;
\r
8777 hp->h_addr_list[0][1] = (char) b1;
\r
8778 hp->h_addr_list[0][2] = (char) b2;
\r
8779 hp->h_addr_list[0][3] = (char) b3;
\r
8785 sa.sin_family = hp->h_addrtype;
\r
8786 uport = (unsigned short) atoi(port);
\r
8787 sa.sin_port = htons(uport);
\r
8788 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8790 /* Make connection */
\r
8791 if (connect(s, (struct sockaddr *) &sa,
\r
8792 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8793 err = WSAGetLastError();
\r
8798 /* Prepare return value */
\r
8799 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8800 cp->kind = CPSock;
\r
8802 *pr = (ProcRef *) cp;
\r
8808 OpenCommPort(char *name, ProcRef *pr)
\r
8813 char fullname[MSG_SIZ];
\r
8815 if (*name != '\\')
\r
8816 sprintf(fullname, "\\\\.\\%s", name);
\r
8818 strcpy(fullname, name);
\r
8820 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8821 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8822 if (h == (HANDLE) -1) {
\r
8823 return GetLastError();
\r
8827 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8829 /* Accumulate characters until a 100ms pause, then parse */
\r
8830 ct.ReadIntervalTimeout = 100;
\r
8831 ct.ReadTotalTimeoutMultiplier = 0;
\r
8832 ct.ReadTotalTimeoutConstant = 0;
\r
8833 ct.WriteTotalTimeoutMultiplier = 0;
\r
8834 ct.WriteTotalTimeoutConstant = 0;
\r
8835 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8837 /* Prepare return value */
\r
8838 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8839 cp->kind = CPComm;
\r
8842 *pr = (ProcRef *) cp;
\r
8848 OpenLoopback(ProcRef *pr)
\r
8850 DisplayFatalError("Not implemented", 0, 1);
\r
8856 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8861 struct sockaddr_in sa, mysa;
\r
8862 struct hostent FAR *hp;
\r
8863 unsigned short uport;
\r
8864 WORD wVersionRequested;
\r
8867 char stderrPortStr[MSG_SIZ];
\r
8869 /* Initialize socket DLL */
\r
8870 wVersionRequested = MAKEWORD(1, 1);
\r
8871 err = WSAStartup(wVersionRequested, &wsaData);
\r
8872 if (err != 0) return err;
\r
8874 /* Resolve remote host name */
\r
8875 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8876 if (!(hp = gethostbyname(host))) {
\r
8877 unsigned int b0, b1, b2, b3;
\r
8879 err = WSAGetLastError();
\r
8881 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8882 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8883 hp->h_addrtype = AF_INET;
\r
8885 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8886 hp->h_addr_list[0] = (char *) malloc(4);
\r
8887 hp->h_addr_list[0][0] = (char) b0;
\r
8888 hp->h_addr_list[0][1] = (char) b1;
\r
8889 hp->h_addr_list[0][2] = (char) b2;
\r
8890 hp->h_addr_list[0][3] = (char) b3;
\r
8896 sa.sin_family = hp->h_addrtype;
\r
8897 uport = (unsigned short) 514;
\r
8898 sa.sin_port = htons(uport);
\r
8899 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8901 /* Bind local socket to unused "privileged" port address
\r
8903 s = INVALID_SOCKET;
\r
8904 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8905 mysa.sin_family = AF_INET;
\r
8906 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8907 for (fromPort = 1023;; fromPort--) {
\r
8908 if (fromPort < 0) {
\r
8910 return WSAEADDRINUSE;
\r
8912 if (s == INVALID_SOCKET) {
\r
8913 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8914 err = WSAGetLastError();
\r
8919 uport = (unsigned short) fromPort;
\r
8920 mysa.sin_port = htons(uport);
\r
8921 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8922 == SOCKET_ERROR) {
\r
8923 err = WSAGetLastError();
\r
8924 if (err == WSAEADDRINUSE) continue;
\r
8928 if (connect(s, (struct sockaddr *) &sa,
\r
8929 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8930 err = WSAGetLastError();
\r
8931 if (err == WSAEADDRINUSE) {
\r
8942 /* Bind stderr local socket to unused "privileged" port address
\r
8944 s2 = INVALID_SOCKET;
\r
8945 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8946 mysa.sin_family = AF_INET;
\r
8947 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8948 for (fromPort = 1023;; fromPort--) {
\r
8949 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8950 if (fromPort < 0) {
\r
8951 (void) closesocket(s);
\r
8953 return WSAEADDRINUSE;
\r
8955 if (s2 == INVALID_SOCKET) {
\r
8956 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8957 err = WSAGetLastError();
\r
8963 uport = (unsigned short) fromPort;
\r
8964 mysa.sin_port = htons(uport);
\r
8965 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8966 == SOCKET_ERROR) {
\r
8967 err = WSAGetLastError();
\r
8968 if (err == WSAEADDRINUSE) continue;
\r
8969 (void) closesocket(s);
\r
8973 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8974 err = WSAGetLastError();
\r
8975 if (err == WSAEADDRINUSE) {
\r
8977 s2 = INVALID_SOCKET;
\r
8980 (void) closesocket(s);
\r
8981 (void) closesocket(s2);
\r
8987 prevStderrPort = fromPort; // remember port used
\r
8988 sprintf(stderrPortStr, "%d", fromPort);
\r
8990 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8991 err = WSAGetLastError();
\r
8992 (void) closesocket(s);
\r
8993 (void) closesocket(s2);
\r
8998 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8999 err = WSAGetLastError();
\r
9000 (void) closesocket(s);
\r
9001 (void) closesocket(s2);
\r
9005 if (*user == NULLCHAR) user = UserName();
\r
9006 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9007 err = WSAGetLastError();
\r
9008 (void) closesocket(s);
\r
9009 (void) closesocket(s2);
\r
9013 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9014 err = WSAGetLastError();
\r
9015 (void) closesocket(s);
\r
9016 (void) closesocket(s2);
\r
9021 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9022 err = WSAGetLastError();
\r
9023 (void) closesocket(s);
\r
9024 (void) closesocket(s2);
\r
9028 (void) closesocket(s2); /* Stop listening */
\r
9030 /* Prepare return value */
\r
9031 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9032 cp->kind = CPRcmd;
\r
9035 *pr = (ProcRef *) cp;
\r
9042 AddInputSource(ProcRef pr, int lineByLine,
\r
9043 InputCallback func, VOIDSTAR closure)
\r
9045 InputSource *is, *is2 = NULL;
\r
9046 ChildProc *cp = (ChildProc *) pr;
\r
9048 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9049 is->lineByLine = lineByLine;
\r
9051 is->closure = closure;
\r
9052 is->second = NULL;
\r
9053 is->next = is->buf;
\r
9054 if (pr == NoProc) {
\r
9055 is->kind = CPReal;
\r
9056 consoleInputSource = is;
\r
9058 is->kind = cp->kind;
\r
9060 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9061 we create all threads suspended so that the is->hThread variable can be
\r
9062 safely assigned, then let the threads start with ResumeThread.
\r
9064 switch (cp->kind) {
\r
9066 is->hFile = cp->hFrom;
\r
9067 cp->hFrom = NULL; /* now owned by InputThread */
\r
9069 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9070 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9074 is->hFile = cp->hFrom;
\r
9075 cp->hFrom = NULL; /* now owned by InputThread */
\r
9077 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9078 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9082 is->sock = cp->sock;
\r
9084 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9085 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9089 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9091 is->sock = cp->sock;
\r
9093 is2->sock = cp->sock2;
\r
9094 is2->second = is2;
\r
9096 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9097 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9099 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9100 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9104 if( is->hThread != NULL ) {
\r
9105 ResumeThread( is->hThread );
\r
9108 if( is2 != NULL && is2->hThread != NULL ) {
\r
9109 ResumeThread( is2->hThread );
\r
9113 return (InputSourceRef) is;
\r
9117 RemoveInputSource(InputSourceRef isr)
\r
9121 is = (InputSource *) isr;
\r
9122 is->hThread = NULL; /* tell thread to stop */
\r
9123 CloseHandle(is->hThread);
\r
9124 if (is->second != NULL) {
\r
9125 is->second->hThread = NULL;
\r
9126 CloseHandle(is->second->hThread);
\r
9130 int no_wrap(char *message, int count)
\r
9132 ConsoleOutput(message, count, FALSE);
\r
9137 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9140 int outCount = SOCKET_ERROR;
\r
9141 ChildProc *cp = (ChildProc *) pr;
\r
9142 static OVERLAPPED ovl;
\r
9143 static int line = 0;
\r
9147 if (appData.noJoin || !appData.useInternalWrap)
\r
9148 return no_wrap(message, count);
\r
9151 int width = get_term_width();
\r
9152 int len = wrap(NULL, message, count, width, &line);
\r
9153 char *msg = malloc(len);
\r
9157 return no_wrap(message, count);
\r
9160 dbgchk = wrap(msg, message, count, width, &line);
\r
9161 if (dbgchk != len && appData.debugMode)
\r
9162 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9163 ConsoleOutput(msg, len, FALSE);
\r
9170 if (ovl.hEvent == NULL) {
\r
9171 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9173 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9175 switch (cp->kind) {
\r
9178 outCount = send(cp->sock, message, count, 0);
\r
9179 if (outCount == SOCKET_ERROR) {
\r
9180 *outError = WSAGetLastError();
\r
9182 *outError = NO_ERROR;
\r
9187 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9188 &dOutCount, NULL)) {
\r
9189 *outError = NO_ERROR;
\r
9190 outCount = (int) dOutCount;
\r
9192 *outError = GetLastError();
\r
9197 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9198 &dOutCount, &ovl);
\r
9199 if (*outError == NO_ERROR) {
\r
9200 outCount = (int) dOutCount;
\r
9208 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9211 /* Ignore delay, not implemented for WinBoard */
\r
9212 return OutputToProcess(pr, message, count, outError);
\r
9217 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9218 char *buf, int count, int error)
\r
9220 DisplayFatalError("Not implemented", 0, 1);
\r
9223 /* see wgamelist.c for Game List functions */
\r
9224 /* see wedittags.c for Edit Tags functions */
\r
9231 char buf[MSG_SIZ];
\r
9234 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9235 f = fopen(buf, "r");
\r
9237 ProcessICSInitScript(f);
\r
9245 StartAnalysisClock()
\r
9247 if (analysisTimerEvent) return;
\r
9248 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9249 (UINT) 2000, NULL);
\r
9253 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9255 highlightInfo.sq[0].x = fromX;
\r
9256 highlightInfo.sq[0].y = fromY;
\r
9257 highlightInfo.sq[1].x = toX;
\r
9258 highlightInfo.sq[1].y = toY;
\r
9264 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9265 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9269 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9271 premoveHighlightInfo.sq[0].x = fromX;
\r
9272 premoveHighlightInfo.sq[0].y = fromY;
\r
9273 premoveHighlightInfo.sq[1].x = toX;
\r
9274 premoveHighlightInfo.sq[1].y = toY;
\r
9278 ClearPremoveHighlights()
\r
9280 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9281 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9285 ShutDownFrontEnd()
\r
9287 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9288 DeleteClipboardTempFiles();
\r
9294 if (IsIconic(hwndMain))
\r
9295 ShowWindow(hwndMain, SW_RESTORE);
\r
9297 SetActiveWindow(hwndMain);
\r
9301 * Prototypes for animation support routines
\r
9303 static void ScreenSquare(int column, int row, POINT * pt);
\r
9304 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9305 POINT frames[], int * nFrames);
\r
9309 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9310 { // [HGM] atomic: animate blast wave
\r
9312 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9313 explodeInfo.fromX = fromX;
\r
9314 explodeInfo.fromY = fromY;
\r
9315 explodeInfo.toX = toX;
\r
9316 explodeInfo.toY = toY;
\r
9317 for(i=1; i<nFrames; i++) {
\r
9318 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9319 DrawPosition(FALSE, NULL);
\r
9320 Sleep(appData.animSpeed);
\r
9322 explodeInfo.radius = 0;
\r
9323 DrawPosition(TRUE, NULL);
\r
9329 AnimateMove(board, fromX, fromY, toX, toY)
\r
9336 ChessSquare piece;
\r
9337 POINT start, finish, mid;
\r
9338 POINT frames[kFactor * 2 + 1];
\r
9341 if (!appData.animate) return;
\r
9342 if (doingSizing) return;
\r
9343 if (fromY < 0 || fromX < 0) return;
\r
9344 piece = board[fromY][fromX];
\r
9345 if (piece >= EmptySquare) return;
\r
9347 ScreenSquare(fromX, fromY, &start);
\r
9348 ScreenSquare(toX, toY, &finish);
\r
9350 /* All pieces except knights move in straight line */
\r
9351 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9352 mid.x = start.x + (finish.x - start.x) / 2;
\r
9353 mid.y = start.y + (finish.y - start.y) / 2;
\r
9355 /* Knight: make diagonal movement then straight */
\r
9356 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9357 mid.x = start.x + (finish.x - start.x) / 2;
\r
9361 mid.y = start.y + (finish.y - start.y) / 2;
\r
9365 /* Don't use as many frames for very short moves */
\r
9366 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9367 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9369 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9371 animInfo.from.x = fromX;
\r
9372 animInfo.from.y = fromY;
\r
9373 animInfo.to.x = toX;
\r
9374 animInfo.to.y = toY;
\r
9375 animInfo.lastpos = start;
\r
9376 animInfo.piece = piece;
\r
9377 for (n = 0; n < nFrames; n++) {
\r
9378 animInfo.pos = frames[n];
\r
9379 DrawPosition(FALSE, NULL);
\r
9380 animInfo.lastpos = animInfo.pos;
\r
9381 Sleep(appData.animSpeed);
\r
9383 animInfo.pos = finish;
\r
9384 DrawPosition(FALSE, NULL);
\r
9385 animInfo.piece = EmptySquare;
\r
9386 if(gameInfo.variant == VariantAtomic &&
\r
9387 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9388 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9391 /* Convert board position to corner of screen rect and color */
\r
9394 ScreenSquare(column, row, pt)
\r
9395 int column; int row; POINT * pt;
\r
9398 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9399 pt->y = lineGap + row * (squareSize + lineGap);
\r
9401 pt->x = lineGap + column * (squareSize + lineGap);
\r
9402 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9406 /* Generate a series of frame coords from start->mid->finish.
\r
9407 The movement rate doubles until the half way point is
\r
9408 reached, then halves back down to the final destination,
\r
9409 which gives a nice slow in/out effect. The algorithmn
\r
9410 may seem to generate too many intermediates for short
\r
9411 moves, but remember that the purpose is to attract the
\r
9412 viewers attention to the piece about to be moved and
\r
9413 then to where it ends up. Too few frames would be less
\r
9417 Tween(start, mid, finish, factor, frames, nFrames)
\r
9418 POINT * start; POINT * mid;
\r
9419 POINT * finish; int factor;
\r
9420 POINT frames[]; int * nFrames;
\r
9422 int n, fraction = 1, count = 0;
\r
9424 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9425 for (n = 0; n < factor; n++)
\r
9427 for (n = 0; n < factor; n++) {
\r
9428 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9429 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9431 fraction = fraction / 2;
\r
9435 frames[count] = *mid;
\r
9438 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9440 for (n = 0; n < factor; n++) {
\r
9441 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9442 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9444 fraction = fraction * 2;
\r
9450 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9452 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9454 EvalGraphSet( first, last, current, pvInfoList );
\r