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
138 typedef struct { // [HGM] atomic
\r
139 int fromX, fromY, toX, toY, radius;
\r
142 static ExplodeInfo explodeInfo;
\r
144 /* Window class names */
\r
145 char szAppName[] = "WinBoard";
\r
146 char szConsoleName[] = "WBConsole";
\r
148 /* Title bar text */
\r
149 char szTitle[] = "WinBoard";
\r
150 char szConsoleTitle[] = "I C S Interaction";
\r
153 char *settingsFileName;
\r
154 Boolean saveSettingsOnExit;
\r
155 char installDir[MSG_SIZ];
\r
156 int errorExitStatus;
\r
158 BoardSize boardSize;
\r
159 Boolean chessProgram;
\r
160 //static int boardX, boardY;
\r
161 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
162 int squareSize, lineGap, minorSize;
\r
163 static int winW, winH;
\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
165 static int logoHeight = 0;
\r
166 static char messageText[MESSAGE_TEXT_MAX];
\r
167 static int clockTimerEvent = 0;
\r
168 static int loadGameTimerEvent = 0;
\r
169 static int analysisTimerEvent = 0;
\r
170 static DelayedEventCallback delayedTimerCallback;
\r
171 static int delayedTimerEvent = 0;
\r
172 static int buttonCount = 2;
\r
173 char *icsTextMenuString;
\r
175 char *firstChessProgramNames;
\r
176 char *secondChessProgramNames;
\r
178 #define PALETTESIZE 256
\r
180 HINSTANCE hInst; /* current instance */
\r
181 Boolean alwaysOnTop = FALSE;
\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
184 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
186 ColorClass currentColorClass;
\r
188 HWND hCommPort = NULL; /* currently open comm port */
\r
189 static HWND hwndPause; /* pause button */
\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
192 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
193 explodeBrush, /* [HGM] atomic */
\r
194 markerBrush, /* [HGM] markers */
\r
195 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
198 static HPEN gridPen = NULL;
\r
199 static HPEN highlightPen = NULL;
\r
200 static HPEN premovePen = NULL;
\r
201 static NPLOGPALETTE pLogPal;
\r
202 static BOOL paletteChanged = FALSE;
\r
203 static HICON iconWhite, iconBlack, iconCurrent;
\r
204 static int doingSizing = FALSE;
\r
205 static int lastSizing = 0;
\r
206 static int prevStderrPort;
\r
207 static HBITMAP userLogo;
\r
209 static HBITMAP liteBackTexture = NULL;
\r
210 static HBITMAP darkBackTexture = NULL;
\r
211 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
212 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
213 static int backTextureSquareSize = 0;
\r
214 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
216 #if __GNUC__ && !defined(_winmajor)
\r
217 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
219 #if defined(_winmajor)
\r
220 #define oldDialog (_winmajor < 4)
\r
222 #define oldDialog 0
\r
232 int cliWidth, cliHeight;
\r
235 SizeInfo sizeInfo[] =
\r
237 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
238 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
239 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
240 { "petite", 33, 1, 1, 1, 0, 0 },
\r
241 { "slim", 37, 2, 1, 0, 0, 0 },
\r
242 { "small", 40, 2, 1, 0, 0, 0 },
\r
243 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
244 { "middling", 49, 2, 0, 0, 0, 0 },
\r
245 { "average", 54, 2, 0, 0, 0, 0 },
\r
246 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
247 { "medium", 64, 3, 0, 0, 0, 0 },
\r
248 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
249 { "large", 80, 3, 0, 0, 0, 0 },
\r
250 { "big", 87, 3, 0, 0, 0, 0 },
\r
251 { "huge", 95, 3, 0, 0, 0, 0 },
\r
252 { "giant", 108, 3, 0, 0, 0, 0 },
\r
253 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
254 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
255 { NULL, 0, 0, 0, 0, 0, 0 }
\r
258 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
259 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
261 { 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
262 { 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
263 { 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
264 { 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
265 { 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
266 { 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
267 { 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
268 { 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
269 { 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
270 { 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
271 { 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
272 { 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
273 { 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
274 { 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
275 { 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
276 { 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
277 { 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
278 { 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
281 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
290 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
291 #define N_BUTTONS 5
\r
293 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
295 {"<<", IDM_ToStart, NULL, NULL},
\r
296 {"<", IDM_Backward, NULL, NULL},
\r
297 {"P", IDM_Pause, NULL, NULL},
\r
298 {">", IDM_Forward, NULL, NULL},
\r
299 {">>", IDM_ToEnd, NULL, NULL},
\r
302 int tinyLayout = 0, smallLayout = 0;
\r
303 #define MENU_BAR_ITEMS 7
\r
304 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
305 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
306 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
310 MySound sounds[(int)NSoundClasses];
\r
311 MyTextAttribs textAttribs[(int)NColorClasses];
\r
313 MyColorizeAttribs colorizeAttribs[] = {
\r
314 { (COLORREF)0, 0, "Shout Text" },
\r
315 { (COLORREF)0, 0, "SShout/CShout" },
\r
316 { (COLORREF)0, 0, "Channel 1 Text" },
\r
317 { (COLORREF)0, 0, "Channel Text" },
\r
318 { (COLORREF)0, 0, "Kibitz Text" },
\r
319 { (COLORREF)0, 0, "Tell Text" },
\r
320 { (COLORREF)0, 0, "Challenge Text" },
\r
321 { (COLORREF)0, 0, "Request Text" },
\r
322 { (COLORREF)0, 0, "Seek Text" },
\r
323 { (COLORREF)0, 0, "Normal Text" },
\r
324 { (COLORREF)0, 0, "None" }
\r
329 static char *commentTitle;
\r
330 static char *commentText;
\r
331 static int commentIndex;
\r
332 static Boolean editComment = FALSE;
\r
335 char errorTitle[MSG_SIZ];
\r
336 char errorMessage[2*MSG_SIZ];
\r
337 HWND errorDialog = NULL;
\r
338 BOOLEAN moveErrorMessageUp = FALSE;
\r
339 BOOLEAN consoleEcho = TRUE;
\r
340 CHARFORMAT consoleCF;
\r
341 COLORREF consoleBackgroundColor;
\r
343 char *programVersion;
\r
349 typedef int CPKind;
\r
358 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
361 #define INPUT_SOURCE_BUF_SIZE 4096
\r
363 typedef struct _InputSource {
\r
370 char buf[INPUT_SOURCE_BUF_SIZE];
\r
374 InputCallback func;
\r
375 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
379 InputSource *consoleInputSource;
\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
385 VOID ConsoleCreate();
\r
387 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
392 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
394 void ParseIcsTextMenu(char *icsTextMenuString);
\r
395 VOID PopUpMoveDialog(char firstchar);
\r
396 VOID PopUpNameDialog(char firstchar);
\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
401 int GameListOptions();
\r
403 int dummy; // [HGM] for obsolete args
\r
405 HWND hwndMain = NULL; /* root window*/
\r
406 HWND hwndConsole = NULL;
\r
407 HWND commentDialog = NULL;
\r
408 HWND moveHistoryDialog = NULL;
\r
409 HWND evalGraphDialog = NULL;
\r
410 HWND engineOutputDialog = NULL;
\r
411 HWND gameListDialog = NULL;
\r
412 HWND editTagsDialog = NULL;
\r
414 int commentUp = FALSE;
\r
416 WindowPlacement wpMain;
\r
417 WindowPlacement wpConsole;
\r
418 WindowPlacement wpComment;
\r
419 WindowPlacement wpMoveHistory;
\r
420 WindowPlacement wpEvalGraph;
\r
421 WindowPlacement wpEngineOutput;
\r
422 WindowPlacement wpGameList;
\r
423 WindowPlacement wpTags;
\r
425 VOID EngineOptionsPopup(); // [HGM] settings
\r
427 VOID GothicPopUp(char *title, VariantClass variant);
\r
429 * Setting "frozen" should disable all user input other than deleting
\r
430 * the window. We do this while engines are initializing themselves.
\r
432 static int frozen = 0;
\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
439 if (frozen) return;
\r
441 hmenu = GetMenu(hwndMain);
\r
442 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
443 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
445 DrawMenuBar(hwndMain);
\r
448 /* Undo a FreezeUI */
\r
454 if (!frozen) return;
\r
456 hmenu = GetMenu(hwndMain);
\r
457 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
458 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
460 DrawMenuBar(hwndMain);
\r
463 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
465 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
471 #define JAWS_ALT_INTERCEPT
\r
472 #define JAWS_KB_NAVIGATION
\r
473 #define JAWS_MENU_ITEMS
\r
474 #define JAWS_SILENCE
\r
475 #define JAWS_REPLAY
\r
477 #define JAWS_COPYRIGHT
\r
478 #define JAWS_DELETE(X) X
\r
479 #define SAYMACHINEMOVE()
\r
483 /*---------------------------------------------------------------------------*\
\r
487 \*---------------------------------------------------------------------------*/
\r
490 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
491 LPSTR lpCmdLine, int nCmdShow)
\r
494 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
495 // INITCOMMONCONTROLSEX ex;
\r
499 LoadLibrary("RICHED32.DLL");
\r
500 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
502 if (!InitApplication(hInstance)) {
\r
505 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
511 // InitCommonControlsEx(&ex);
\r
512 InitCommonControls();
\r
514 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
515 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
516 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
518 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
520 while (GetMessage(&msg, /* message structure */
\r
521 NULL, /* handle of window receiving the message */
\r
522 0, /* lowest message to examine */
\r
523 0)) /* highest message to examine */
\r
526 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
527 // [HGM] navigate: switch between all windows with tab
\r
528 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
529 int i, currentElement = 0;
\r
531 // first determine what element of the chain we come from (if any)
\r
532 if(appData.icsActive) {
\r
533 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
534 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
536 if(engineOutputDialog && EngineOutputIsUp()) {
\r
537 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
538 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
540 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
541 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
543 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
544 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
545 if(msg.hwnd == e1) currentElement = 2; else
\r
546 if(msg.hwnd == e2) currentElement = 3; else
\r
547 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
548 if(msg.hwnd == mh) currentElement = 4; else
\r
549 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
550 if(msg.hwnd == hText) currentElement = 5; else
\r
551 if(msg.hwnd == hInput) currentElement = 6; else
\r
552 for (i = 0; i < N_BUTTONS; i++) {
\r
553 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
556 // determine where to go to
\r
557 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
559 currentElement = (currentElement + direction) % 7;
\r
560 switch(currentElement) {
\r
562 h = hwndMain; break; // passing this case always makes the loop exit
\r
564 h = buttonDesc[0].hwnd; break; // could be NULL
\r
566 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
569 if(!EngineOutputIsUp()) continue;
\r
572 if(!MoveHistoryIsUp()) continue;
\r
574 // case 6: // input to eval graph does not seem to get here!
\r
575 // if(!EvalGraphIsUp()) continue;
\r
576 // h = evalGraphDialog; break;
\r
578 if(!appData.icsActive) continue;
\r
582 if(!appData.icsActive) continue;
\r
588 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
589 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
592 continue; // this message now has been processed
\r
596 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
597 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
598 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
599 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
600 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
601 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
602 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
603 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
604 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
605 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
606 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
607 for(i=0; i<MAX_CHAT; i++)
\r
608 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
611 if(done) continue; // [HGM] chat: end patch
\r
612 TranslateMessage(&msg); /* Translates virtual key codes */
\r
613 DispatchMessage(&msg); /* Dispatches message to window */
\r
618 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
621 /*---------------------------------------------------------------------------*\
\r
623 * Initialization functions
\r
625 \*---------------------------------------------------------------------------*/
\r
629 { // update user logo if necessary
\r
630 static char oldUserName[MSG_SIZ], *curName;
\r
632 if(appData.autoLogo) {
\r
633 curName = UserName();
\r
634 if(strcmp(curName, oldUserName)) {
\r
635 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
636 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
637 strcpy(oldUserName, curName);
\r
643 InitApplication(HINSTANCE hInstance)
\r
647 /* Fill in window class structure with parameters that describe the */
\r
650 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
651 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
652 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
653 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
654 wc.hInstance = hInstance; /* Owner of this class */
\r
655 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
656 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
657 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
658 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
659 wc.lpszClassName = szAppName; /* Name to register as */
\r
661 /* Register the window class and return success/failure code. */
\r
662 if (!RegisterClass(&wc)) return FALSE;
\r
664 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
665 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
667 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
668 wc.hInstance = hInstance;
\r
669 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
670 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
671 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
672 wc.lpszMenuName = NULL;
\r
673 wc.lpszClassName = szConsoleName;
\r
675 if (!RegisterClass(&wc)) return FALSE;
\r
680 /* Set by InitInstance, used by EnsureOnScreen */
\r
681 int screenHeight, screenWidth;
\r
684 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
686 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
687 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
688 if (*x > screenWidth - 32) *x = 0;
\r
689 if (*y > screenHeight - 32) *y = 0;
\r
690 if (*x < minX) *x = minX;
\r
691 if (*y < minY) *y = minY;
\r
695 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
697 HWND hwnd; /* Main window handle. */
\r
699 WINDOWPLACEMENT wp;
\r
702 hInst = hInstance; /* Store instance handle in our global variable */
\r
703 programName = szAppName;
\r
705 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
706 *filepart = NULLCHAR;
\r
708 GetCurrentDirectory(MSG_SIZ, installDir);
\r
710 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
711 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
712 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
713 /* xboard, and older WinBoards, controlled the move sound with the
\r
714 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
715 always turn the option on (so that the backend will call us),
\r
716 then let the user turn the sound off by setting it to silence if
\r
717 desired. To accommodate old winboard.ini files saved by old
\r
718 versions of WinBoard, we also turn off the sound if the option
\r
719 was initially set to false. [HGM] taken out of InitAppData */
\r
720 if (!appData.ringBellAfterMoves) {
\r
721 sounds[(int)SoundMove].name = strdup("");
\r
722 appData.ringBellAfterMoves = TRUE;
\r
724 if (appData.debugMode) {
\r
725 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
726 setbuf(debugFP, NULL);
\r
731 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
732 // InitEngineUCI( installDir, &second );
\r
734 /* Create a main window for this application instance. */
\r
735 hwnd = CreateWindow(szAppName, szTitle,
\r
736 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
737 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
738 NULL, NULL, hInstance, NULL);
\r
741 /* If window could not be created, return "failure" */
\r
746 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
747 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
748 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
750 if (first.programLogo == NULL && appData.debugMode) {
\r
751 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
753 } else if(appData.autoLogo) {
\r
754 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
756 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
757 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
761 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
762 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
764 if (second.programLogo == NULL && appData.debugMode) {
\r
765 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
767 } else if(appData.autoLogo) {
\r
769 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
770 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
771 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
773 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
774 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
775 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
781 iconWhite = LoadIcon(hInstance, "icon_white");
\r
782 iconBlack = LoadIcon(hInstance, "icon_black");
\r
783 iconCurrent = iconWhite;
\r
784 InitDrawingColors();
\r
785 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
786 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
787 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
788 /* Compute window size for each board size, and use the largest
\r
789 size that fits on this screen as the default. */
\r
790 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
791 if (boardSize == (BoardSize)-1 &&
\r
792 winH <= screenHeight
\r
793 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
794 && winW <= screenWidth) {
\r
795 boardSize = (BoardSize)ibs;
\r
799 InitDrawingSizes(boardSize, 0);
\r
801 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
803 /* [AS] Load textures if specified */
\r
804 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
806 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
807 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
808 liteBackTextureMode = appData.liteBackTextureMode;
\r
810 if (liteBackTexture == NULL && appData.debugMode) {
\r
811 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
815 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
816 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
817 darkBackTextureMode = appData.darkBackTextureMode;
\r
819 if (darkBackTexture == NULL && appData.debugMode) {
\r
820 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
824 mysrandom( (unsigned) time(NULL) );
\r
826 /* [AS] Restore layout */
\r
827 if( wpMoveHistory.visible ) {
\r
828 MoveHistoryPopUp();
\r
831 if( wpEvalGraph.visible ) {
\r
835 if( wpEngineOutput.visible ) {
\r
836 EngineOutputPopUp();
\r
839 /* Make the window visible; update its client area; and return "success" */
\r
840 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
841 wp.length = sizeof(WINDOWPLACEMENT);
\r
843 wp.showCmd = nCmdShow;
\r
844 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
845 wp.rcNormalPosition.left = wpMain.x;
\r
846 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
847 wp.rcNormalPosition.top = wpMain.y;
\r
848 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
849 SetWindowPlacement(hwndMain, &wp);
\r
851 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
853 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
854 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
858 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
859 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
861 ShowWindow(hwndConsole, nCmdShow);
\r
862 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
863 char buf[MSG_SIZ], *p = buf, *q;
\r
864 strcpy(buf, appData.chatBoxes);
\r
866 q = strchr(p, ';');
\r
868 if(*p) ChatPopUp(p);
\r
871 SetActiveWindow(hwndConsole);
\r
873 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
874 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
883 HMENU hmenu = GetMenu(hwndMain);
\r
885 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
886 MF_BYCOMMAND|((appData.icsActive &&
\r
887 *appData.icsCommPort != NULLCHAR) ?
\r
888 MF_ENABLED : MF_GRAYED));
\r
889 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
890 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
891 MF_CHECKED : MF_UNCHECKED));
\r
894 //---------------------------------------------------------------------------------------------------------
\r
896 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
897 #define XBOARD FALSE
\r
899 #define OPTCHAR "/"
\r
900 #define SEPCHAR "="
\r
904 // front-end part of option handling
\r
907 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
909 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
910 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
913 lf->lfEscapement = 0;
\r
914 lf->lfOrientation = 0;
\r
915 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
916 lf->lfItalic = mfp->italic;
\r
917 lf->lfUnderline = mfp->underline;
\r
918 lf->lfStrikeOut = mfp->strikeout;
\r
919 lf->lfCharSet = mfp->charset;
\r
920 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
921 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
922 lf->lfQuality = DEFAULT_QUALITY;
\r
923 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
924 strcpy(lf->lfFaceName, mfp->faceName);
\r
928 CreateFontInMF(MyFont *mf)
\r
930 LFfromMFP(&mf->lf, &mf->mfp);
\r
931 if (mf->hf) DeleteObject(mf->hf);
\r
932 mf->hf = CreateFontIndirect(&mf->lf);
\r
935 // [HGM] This platform-dependent table provides the location for storing the color info
\r
937 colorVariable[] = {
\r
942 &highlightSquareColor,
\r
943 &premoveHighlightColor,
\r
945 &consoleBackgroundColor,
\r
946 &appData.fontForeColorWhite,
\r
947 &appData.fontBackColorWhite,
\r
948 &appData.fontForeColorBlack,
\r
949 &appData.fontBackColorBlack,
\r
950 &appData.evalHistColorWhite,
\r
951 &appData.evalHistColorBlack,
\r
952 &appData.highlightArrowColor,
\r
955 /* Command line font name parser. NULL name means do nothing.
\r
956 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
957 For backward compatibility, syntax without the colon is also
\r
958 accepted, but font names with digits in them won't work in that case.
\r
961 ParseFontName(char *name, MyFontParams *mfp)
\r
964 if (name == NULL) return;
\r
966 q = strchr(p, ':');
\r
968 if (q - p >= sizeof(mfp->faceName))
\r
969 ExitArgError("Font name too long:", name);
\r
970 memcpy(mfp->faceName, p, q - p);
\r
971 mfp->faceName[q - p] = NULLCHAR;
\r
975 while (*p && !isdigit(*p)) {
\r
977 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
978 ExitArgError("Font name too long:", name);
\r
980 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
983 if (!*p) ExitArgError("Font point size missing:", name);
\r
984 mfp->pointSize = (float) atof(p);
\r
985 mfp->bold = (strchr(p, 'b') != NULL);
\r
986 mfp->italic = (strchr(p, 'i') != NULL);
\r
987 mfp->underline = (strchr(p, 'u') != NULL);
\r
988 mfp->strikeout = (strchr(p, 's') != NULL);
\r
989 mfp->charset = DEFAULT_CHARSET;
\r
990 q = strchr(p, 'c');
\r
992 mfp->charset = (BYTE) atoi(q+1);
\r
996 ParseFont(char *name, int number)
\r
997 { // wrapper to shield back-end from 'font'
\r
998 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1003 { // in WB we have a 2D array of fonts; this initializes their description
\r
1005 /* Point font array elements to structures and
\r
1006 parse default font names */
\r
1007 for (i=0; i<NUM_FONTS; i++) {
\r
1008 for (j=0; j<NUM_SIZES; j++) {
\r
1009 font[j][i] = &fontRec[j][i];
\r
1010 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1017 { // here we create the actual fonts from the selected descriptions
\r
1019 for (i=0; i<NUM_FONTS; i++) {
\r
1020 for (j=0; j<NUM_SIZES; j++) {
\r
1021 CreateFontInMF(font[j][i]);
\r
1025 /* Color name parser.
\r
1026 X version accepts X color names, but this one
\r
1027 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1029 ParseColorName(char *name)
\r
1031 int red, green, blue, count;
\r
1032 char buf[MSG_SIZ];
\r
1034 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1036 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1037 &red, &green, &blue);
\r
1040 sprintf(buf, "Can't parse color name %s", name);
\r
1041 DisplayError(buf, 0);
\r
1042 return RGB(0, 0, 0);
\r
1044 return PALETTERGB(red, green, blue);
\r
1048 ParseColor(int n, char *name)
\r
1049 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1050 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1054 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1056 char *e = argValue;
\r
1060 if (*e == 'b') eff |= CFE_BOLD;
\r
1061 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1062 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1063 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1064 else if (*e == '#' || isdigit(*e)) break;
\r
1068 *color = ParseColorName(e);
\r
1072 ParseTextAttribs(ColorClass cc, char *s)
\r
1073 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1074 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1075 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1079 ParseBoardSize(void *addr, char *name)
\r
1080 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1081 BoardSize bs = SizeTiny;
\r
1082 while (sizeInfo[bs].name != NULL) {
\r
1083 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1084 *(BoardSize *)addr = bs;
\r
1089 ExitArgError("Unrecognized board size value", name);
\r
1094 { // [HGM] import name from appData first
\r
1097 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1098 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1099 textAttribs[cc].sound.data = NULL;
\r
1100 MyLoadSound(&textAttribs[cc].sound);
\r
1102 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1103 textAttribs[cc].sound.name = strdup("");
\r
1104 textAttribs[cc].sound.data = NULL;
\r
1106 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1107 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1108 sounds[sc].data = NULL;
\r
1109 MyLoadSound(&sounds[sc]);
\r
1114 SetCommPortDefaults()
\r
1116 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1117 dcb.DCBlength = sizeof(DCB);
\r
1118 dcb.BaudRate = 9600;
\r
1119 dcb.fBinary = TRUE;
\r
1120 dcb.fParity = FALSE;
\r
1121 dcb.fOutxCtsFlow = FALSE;
\r
1122 dcb.fOutxDsrFlow = FALSE;
\r
1123 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1124 dcb.fDsrSensitivity = FALSE;
\r
1125 dcb.fTXContinueOnXoff = TRUE;
\r
1126 dcb.fOutX = FALSE;
\r
1128 dcb.fNull = FALSE;
\r
1129 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1130 dcb.fAbortOnError = FALSE;
\r
1132 dcb.Parity = SPACEPARITY;
\r
1133 dcb.StopBits = ONESTOPBIT;
\r
1136 // [HGM] args: these three cases taken out to stay in front-end
\r
1138 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1139 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1140 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1141 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1143 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1144 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1145 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1146 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1147 ad->argName, mfp->faceName, mfp->pointSize,
\r
1148 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1149 mfp->bold ? "b" : "",
\r
1150 mfp->italic ? "i" : "",
\r
1151 mfp->underline ? "u" : "",
\r
1152 mfp->strikeout ? "s" : "",
\r
1153 (int)mfp->charset);
\r
1159 { // [HGM] copy the names from the internal WB variables to appData
\r
1162 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1163 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1164 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1165 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1169 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1170 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1171 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1172 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1173 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1174 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1175 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1176 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1177 (ta->effects) ? " " : "",
\r
1178 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1182 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1183 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1184 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1185 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1186 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1190 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1191 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1192 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1196 ParseCommPortSettings(char *s)
\r
1197 { // wrapper to keep dcb from back-end
\r
1198 ParseCommSettings(s, &dcb);
\r
1203 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1204 GetActualPlacement(hwndMain, &wpMain);
\r
1205 GetActualPlacement(hwndConsole, &wpConsole);
\r
1206 GetActualPlacement(commentDialog, &wpComment);
\r
1207 GetActualPlacement(editTagsDialog, &wpTags);
\r
1208 GetActualPlacement(gameListDialog, &wpGameList);
\r
1209 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1210 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1211 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1215 PrintCommPortSettings(FILE *f, char *name)
\r
1216 { // wrapper to shield back-end from DCB
\r
1217 PrintCommSettings(f, name, &dcb);
\r
1221 MySearchPath(char *installDir, char *name, char *fullname)
\r
1224 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1228 MyGetFullPathName(char *name, char *fullname)
\r
1231 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1236 { // [HGM] args: allows testing if main window is realized from back-end
\r
1237 return hwndMain != NULL;
\r
1241 PopUpStartupDialog()
\r
1245 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1246 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1247 FreeProcInstance(lpProc);
\r
1250 /*---------------------------------------------------------------------------*\
\r
1252 * GDI board drawing routines
\r
1254 \*---------------------------------------------------------------------------*/
\r
1256 /* [AS] Draw square using background texture */
\r
1257 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1262 return; /* Should never happen! */
\r
1265 SetGraphicsMode( dst, GM_ADVANCED );
\r
1272 /* X reflection */
\r
1277 x.eDx = (FLOAT) dw + dx - 1;
\r
1280 SetWorldTransform( dst, &x );
\r
1283 /* Y reflection */
\r
1289 x.eDy = (FLOAT) dh + dy - 1;
\r
1291 SetWorldTransform( dst, &x );
\r
1299 x.eDx = (FLOAT) dx;
\r
1300 x.eDy = (FLOAT) dy;
\r
1303 SetWorldTransform( dst, &x );
\r
1307 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1315 SetWorldTransform( dst, &x );
\r
1317 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1320 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1322 PM_WP = (int) WhitePawn,
\r
1323 PM_WN = (int) WhiteKnight,
\r
1324 PM_WB = (int) WhiteBishop,
\r
1325 PM_WR = (int) WhiteRook,
\r
1326 PM_WQ = (int) WhiteQueen,
\r
1327 PM_WF = (int) WhiteFerz,
\r
1328 PM_WW = (int) WhiteWazir,
\r
1329 PM_WE = (int) WhiteAlfil,
\r
1330 PM_WM = (int) WhiteMan,
\r
1331 PM_WO = (int) WhiteCannon,
\r
1332 PM_WU = (int) WhiteUnicorn,
\r
1333 PM_WH = (int) WhiteNightrider,
\r
1334 PM_WA = (int) WhiteAngel,
\r
1335 PM_WC = (int) WhiteMarshall,
\r
1336 PM_WAB = (int) WhiteCardinal,
\r
1337 PM_WD = (int) WhiteDragon,
\r
1338 PM_WL = (int) WhiteLance,
\r
1339 PM_WS = (int) WhiteCobra,
\r
1340 PM_WV = (int) WhiteFalcon,
\r
1341 PM_WSG = (int) WhiteSilver,
\r
1342 PM_WG = (int) WhiteGrasshopper,
\r
1343 PM_WK = (int) WhiteKing,
\r
1344 PM_BP = (int) BlackPawn,
\r
1345 PM_BN = (int) BlackKnight,
\r
1346 PM_BB = (int) BlackBishop,
\r
1347 PM_BR = (int) BlackRook,
\r
1348 PM_BQ = (int) BlackQueen,
\r
1349 PM_BF = (int) BlackFerz,
\r
1350 PM_BW = (int) BlackWazir,
\r
1351 PM_BE = (int) BlackAlfil,
\r
1352 PM_BM = (int) BlackMan,
\r
1353 PM_BO = (int) BlackCannon,
\r
1354 PM_BU = (int) BlackUnicorn,
\r
1355 PM_BH = (int) BlackNightrider,
\r
1356 PM_BA = (int) BlackAngel,
\r
1357 PM_BC = (int) BlackMarshall,
\r
1358 PM_BG = (int) BlackGrasshopper,
\r
1359 PM_BAB = (int) BlackCardinal,
\r
1360 PM_BD = (int) BlackDragon,
\r
1361 PM_BL = (int) BlackLance,
\r
1362 PM_BS = (int) BlackCobra,
\r
1363 PM_BV = (int) BlackFalcon,
\r
1364 PM_BSG = (int) BlackSilver,
\r
1365 PM_BK = (int) BlackKing
\r
1368 static HFONT hPieceFont = NULL;
\r
1369 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1370 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1371 static int fontBitmapSquareSize = 0;
\r
1372 static char pieceToFontChar[(int) EmptySquare] =
\r
1373 { 'p', 'n', 'b', 'r', 'q',
\r
1374 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1375 'k', 'o', 'm', 'v', 't', 'w',
\r
1376 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1379 extern BOOL SetCharTable( char *table, const char * map );
\r
1380 /* [HGM] moved to backend.c */
\r
1382 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1385 BYTE r1 = GetRValue( color );
\r
1386 BYTE g1 = GetGValue( color );
\r
1387 BYTE b1 = GetBValue( color );
\r
1393 /* Create a uniform background first */
\r
1394 hbrush = CreateSolidBrush( color );
\r
1395 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1396 FillRect( hdc, &rc, hbrush );
\r
1397 DeleteObject( hbrush );
\r
1400 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1401 int steps = squareSize / 2;
\r
1404 for( i=0; i<steps; i++ ) {
\r
1405 BYTE r = r1 - (r1-r2) * i / steps;
\r
1406 BYTE g = g1 - (g1-g2) * i / steps;
\r
1407 BYTE b = b1 - (b1-b2) * i / steps;
\r
1409 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1410 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1411 FillRect( hdc, &rc, hbrush );
\r
1412 DeleteObject(hbrush);
\r
1415 else if( mode == 2 ) {
\r
1416 /* Diagonal gradient, good more or less for every piece */
\r
1417 POINT triangle[3];
\r
1418 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1419 HBRUSH hbrush_old;
\r
1420 int steps = squareSize;
\r
1423 triangle[0].x = squareSize - steps;
\r
1424 triangle[0].y = squareSize;
\r
1425 triangle[1].x = squareSize;
\r
1426 triangle[1].y = squareSize;
\r
1427 triangle[2].x = squareSize;
\r
1428 triangle[2].y = squareSize - steps;
\r
1430 for( i=0; i<steps; i++ ) {
\r
1431 BYTE r = r1 - (r1-r2) * i / steps;
\r
1432 BYTE g = g1 - (g1-g2) * i / steps;
\r
1433 BYTE b = b1 - (b1-b2) * i / steps;
\r
1435 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1436 hbrush_old = SelectObject( hdc, hbrush );
\r
1437 Polygon( hdc, triangle, 3 );
\r
1438 SelectObject( hdc, hbrush_old );
\r
1439 DeleteObject(hbrush);
\r
1444 SelectObject( hdc, hpen );
\r
1449 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1450 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1451 piece: follow the steps as explained below.
\r
1453 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1457 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1461 int backColor = whitePieceColor;
\r
1462 int foreColor = blackPieceColor;
\r
1464 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1465 backColor = appData.fontBackColorWhite;
\r
1466 foreColor = appData.fontForeColorWhite;
\r
1468 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1469 backColor = appData.fontBackColorBlack;
\r
1470 foreColor = appData.fontForeColorBlack;
\r
1474 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1476 hbm_old = SelectObject( hdc, hbm );
\r
1480 rc.right = squareSize;
\r
1481 rc.bottom = squareSize;
\r
1483 /* Step 1: background is now black */
\r
1484 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1486 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1488 pt.x = (squareSize - sz.cx) / 2;
\r
1489 pt.y = (squareSize - sz.cy) / 2;
\r
1491 SetBkMode( hdc, TRANSPARENT );
\r
1492 SetTextColor( hdc, chroma );
\r
1493 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1494 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1496 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1497 /* Step 3: the area outside the piece is filled with white */
\r
1498 // FloodFill( hdc, 0, 0, chroma );
\r
1499 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1500 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1501 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1502 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1503 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1505 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1506 but if the start point is not inside the piece we're lost!
\r
1507 There should be a better way to do this... if we could create a region or path
\r
1508 from the fill operation we would be fine for example.
\r
1510 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1511 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1513 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1514 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1515 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1517 SelectObject( dc2, bm2 );
\r
1518 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1519 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1520 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1521 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1522 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1525 DeleteObject( bm2 );
\r
1528 SetTextColor( hdc, 0 );
\r
1530 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1531 draw the piece again in black for safety.
\r
1533 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1535 SelectObject( hdc, hbm_old );
\r
1537 if( hPieceMask[index] != NULL ) {
\r
1538 DeleteObject( hPieceMask[index] );
\r
1541 hPieceMask[index] = hbm;
\r
1544 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1546 SelectObject( hdc, hbm );
\r
1549 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1550 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1551 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1553 SelectObject( dc1, hPieceMask[index] );
\r
1554 SelectObject( dc2, bm2 );
\r
1555 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1556 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1559 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1560 the piece background and deletes (makes transparent) the rest.
\r
1561 Thanks to that mask, we are free to paint the background with the greates
\r
1562 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1563 We use this, to make gradients and give the pieces a "roundish" look.
\r
1565 SetPieceBackground( hdc, backColor, 2 );
\r
1566 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1570 DeleteObject( bm2 );
\r
1573 SetTextColor( hdc, foreColor );
\r
1574 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1576 SelectObject( hdc, hbm_old );
\r
1578 if( hPieceFace[index] != NULL ) {
\r
1579 DeleteObject( hPieceFace[index] );
\r
1582 hPieceFace[index] = hbm;
\r
1585 static int TranslatePieceToFontPiece( int piece )
\r
1615 case BlackMarshall:
\r
1619 case BlackNightrider:
\r
1625 case BlackUnicorn:
\r
1629 case BlackGrasshopper:
\r
1641 case BlackCardinal:
\r
1648 case WhiteMarshall:
\r
1652 case WhiteNightrider:
\r
1658 case WhiteUnicorn:
\r
1662 case WhiteGrasshopper:
\r
1674 case WhiteCardinal:
\r
1683 void CreatePiecesFromFont()
\r
1686 HDC hdc_window = NULL;
\r
1692 if( fontBitmapSquareSize < 0 ) {
\r
1693 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1697 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1698 fontBitmapSquareSize = -1;
\r
1702 if( fontBitmapSquareSize != squareSize ) {
\r
1703 hdc_window = GetDC( hwndMain );
\r
1704 hdc = CreateCompatibleDC( hdc_window );
\r
1706 if( hPieceFont != NULL ) {
\r
1707 DeleteObject( hPieceFont );
\r
1710 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1711 hPieceMask[i] = NULL;
\r
1712 hPieceFace[i] = NULL;
\r
1718 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1719 fontHeight = appData.fontPieceSize;
\r
1722 fontHeight = (fontHeight * squareSize) / 100;
\r
1724 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1726 lf.lfEscapement = 0;
\r
1727 lf.lfOrientation = 0;
\r
1728 lf.lfWeight = FW_NORMAL;
\r
1730 lf.lfUnderline = 0;
\r
1731 lf.lfStrikeOut = 0;
\r
1732 lf.lfCharSet = DEFAULT_CHARSET;
\r
1733 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1734 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1735 lf.lfQuality = PROOF_QUALITY;
\r
1736 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1737 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1738 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1740 hPieceFont = CreateFontIndirect( &lf );
\r
1742 if( hPieceFont == NULL ) {
\r
1743 fontBitmapSquareSize = -2;
\r
1746 /* Setup font-to-piece character table */
\r
1747 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1748 /* No (or wrong) global settings, try to detect the font */
\r
1749 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1751 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1753 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1754 /* DiagramTT* family */
\r
1755 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1757 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1758 /* Fairy symbols */
\r
1759 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1761 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1762 /* Good Companion (Some characters get warped as literal :-( */
\r
1763 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1764 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1765 SetCharTable(pieceToFontChar, s);
\r
1768 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1769 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1773 /* Create bitmaps */
\r
1774 hfont_old = SelectObject( hdc, hPieceFont );
\r
1775 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1776 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1777 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1779 SelectObject( hdc, hfont_old );
\r
1781 fontBitmapSquareSize = squareSize;
\r
1785 if( hdc != NULL ) {
\r
1789 if( hdc_window != NULL ) {
\r
1790 ReleaseDC( hwndMain, hdc_window );
\r
1795 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1799 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1800 if (gameInfo.event &&
\r
1801 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1802 strcmp(name, "k80s") == 0) {
\r
1803 strcpy(name, "tim");
\r
1805 return LoadBitmap(hinst, name);
\r
1809 /* Insert a color into the program's logical palette
\r
1810 structure. This code assumes the given color is
\r
1811 the result of the RGB or PALETTERGB macro, and it
\r
1812 knows how those macros work (which is documented).
\r
1815 InsertInPalette(COLORREF color)
\r
1817 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1819 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1820 DisplayFatalError("Too many colors", 0, 1);
\r
1821 pLogPal->palNumEntries--;
\r
1825 pe->peFlags = (char) 0;
\r
1826 pe->peRed = (char) (0xFF & color);
\r
1827 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1828 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1834 InitDrawingColors()
\r
1836 if (pLogPal == NULL) {
\r
1837 /* Allocate enough memory for a logical palette with
\r
1838 * PALETTESIZE entries and set the size and version fields
\r
1839 * of the logical palette structure.
\r
1841 pLogPal = (NPLOGPALETTE)
\r
1842 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1843 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1844 pLogPal->palVersion = 0x300;
\r
1846 pLogPal->palNumEntries = 0;
\r
1848 InsertInPalette(lightSquareColor);
\r
1849 InsertInPalette(darkSquareColor);
\r
1850 InsertInPalette(whitePieceColor);
\r
1851 InsertInPalette(blackPieceColor);
\r
1852 InsertInPalette(highlightSquareColor);
\r
1853 InsertInPalette(premoveHighlightColor);
\r
1855 /* create a logical color palette according the information
\r
1856 * in the LOGPALETTE structure.
\r
1858 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1860 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1861 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1862 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1863 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1864 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1865 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1866 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1867 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1868 /* [AS] Force rendering of the font-based pieces */
\r
1869 if( fontBitmapSquareSize > 0 ) {
\r
1870 fontBitmapSquareSize = 0;
\r
1876 BoardWidth(int boardSize, int n)
\r
1877 { /* [HGM] argument n added to allow different width and height */
\r
1878 int lineGap = sizeInfo[boardSize].lineGap;
\r
1880 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1881 lineGap = appData.overrideLineGap;
\r
1884 return (n + 1) * lineGap +
\r
1885 n * sizeInfo[boardSize].squareSize;
\r
1888 /* Respond to board resize by dragging edge */
\r
1890 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1892 BoardSize newSize = NUM_SIZES - 1;
\r
1893 static int recurse = 0;
\r
1894 if (IsIconic(hwndMain)) return;
\r
1895 if (recurse > 0) return;
\r
1897 while (newSize > 0) {
\r
1898 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1899 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1900 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1903 boardSize = newSize;
\r
1904 InitDrawingSizes(boardSize, flags);
\r
1909 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1912 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1914 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1915 ChessSquare piece;
\r
1916 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1918 SIZE clockSize, messageSize;
\r
1920 char buf[MSG_SIZ];
\r
1922 HMENU hmenu = GetMenu(hwndMain);
\r
1923 RECT crect, wrect, oldRect;
\r
1925 LOGBRUSH logbrush;
\r
1927 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1928 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1930 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1931 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1933 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1934 oldRect.top = wpMain.y;
\r
1935 oldRect.right = wpMain.x + wpMain.width;
\r
1936 oldRect.bottom = wpMain.y + wpMain.height;
\r
1938 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1939 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1940 squareSize = sizeInfo[boardSize].squareSize;
\r
1941 lineGap = sizeInfo[boardSize].lineGap;
\r
1942 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1944 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1945 lineGap = appData.overrideLineGap;
\r
1948 if (tinyLayout != oldTinyLayout) {
\r
1949 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1951 style &= ~WS_SYSMENU;
\r
1952 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1953 "&Minimize\tCtrl+F4");
\r
1955 style |= WS_SYSMENU;
\r
1956 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1958 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1960 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1961 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1962 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1964 DrawMenuBar(hwndMain);
\r
1967 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1968 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1970 /* Get text area sizes */
\r
1971 hdc = GetDC(hwndMain);
\r
1972 if (appData.clockMode) {
\r
1973 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1975 sprintf(buf, "White");
\r
1977 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1978 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1979 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1980 str = "We only care about the height here";
\r
1981 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1982 SelectObject(hdc, oldFont);
\r
1983 ReleaseDC(hwndMain, hdc);
\r
1985 /* Compute where everything goes */
\r
1986 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1987 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1988 logoHeight = 2*clockSize.cy;
\r
1989 leftLogoRect.left = OUTER_MARGIN;
\r
1990 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1991 leftLogoRect.top = OUTER_MARGIN;
\r
1992 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1994 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1995 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1996 rightLogoRect.top = OUTER_MARGIN;
\r
1997 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2000 whiteRect.left = leftLogoRect.right;
\r
2001 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2002 whiteRect.top = OUTER_MARGIN;
\r
2003 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2005 blackRect.right = rightLogoRect.left;
\r
2006 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2007 blackRect.top = whiteRect.top;
\r
2008 blackRect.bottom = whiteRect.bottom;
\r
2010 whiteRect.left = OUTER_MARGIN;
\r
2011 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2012 whiteRect.top = OUTER_MARGIN;
\r
2013 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2017 blackRect.top = whiteRect.top;
\r
2018 blackRect.bottom = whiteRect.bottom;
\r
2020 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2023 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2024 if (appData.showButtonBar) {
\r
2025 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2026 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2028 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2030 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2031 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2033 boardRect.left = OUTER_MARGIN;
\r
2034 boardRect.right = boardRect.left + boardWidth;
\r
2035 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2036 boardRect.bottom = boardRect.top + boardHeight;
\r
2038 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2039 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2040 oldBoardSize = boardSize;
\r
2041 oldTinyLayout = tinyLayout;
\r
2042 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2043 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2044 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2045 winW *= 1 + twoBoards;
\r
2046 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2047 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2048 wpMain.height = winH; // without disturbing window attachments
\r
2049 GetWindowRect(hwndMain, &wrect);
\r
2050 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2051 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2053 // [HGM] placement: let attached windows follow size change.
\r
2054 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2055 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2056 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2057 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2058 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2060 /* compensate if menu bar wrapped */
\r
2061 GetClientRect(hwndMain, &crect);
\r
2062 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2063 wpMain.height += offby;
\r
2065 case WMSZ_TOPLEFT:
\r
2066 SetWindowPos(hwndMain, NULL,
\r
2067 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2068 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2071 case WMSZ_TOPRIGHT:
\r
2073 SetWindowPos(hwndMain, NULL,
\r
2074 wrect.left, wrect.bottom - wpMain.height,
\r
2075 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2078 case WMSZ_BOTTOMLEFT:
\r
2080 SetWindowPos(hwndMain, NULL,
\r
2081 wrect.right - wpMain.width, wrect.top,
\r
2082 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2085 case WMSZ_BOTTOMRIGHT:
\r
2089 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2090 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2095 for (i = 0; i < N_BUTTONS; i++) {
\r
2096 if (buttonDesc[i].hwnd != NULL) {
\r
2097 DestroyWindow(buttonDesc[i].hwnd);
\r
2098 buttonDesc[i].hwnd = NULL;
\r
2100 if (appData.showButtonBar) {
\r
2101 buttonDesc[i].hwnd =
\r
2102 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2103 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2104 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2105 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2106 (HMENU) buttonDesc[i].id,
\r
2107 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2109 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2110 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2111 MAKELPARAM(FALSE, 0));
\r
2113 if (buttonDesc[i].id == IDM_Pause)
\r
2114 hwndPause = buttonDesc[i].hwnd;
\r
2115 buttonDesc[i].wndproc = (WNDPROC)
\r
2116 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2119 if (gridPen != NULL) DeleteObject(gridPen);
\r
2120 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2121 if (premovePen != NULL) DeleteObject(premovePen);
\r
2122 if (lineGap != 0) {
\r
2123 logbrush.lbStyle = BS_SOLID;
\r
2124 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2126 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2127 lineGap, &logbrush, 0, NULL);
\r
2128 logbrush.lbColor = highlightSquareColor;
\r
2130 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2131 lineGap, &logbrush, 0, NULL);
\r
2133 logbrush.lbColor = premoveHighlightColor;
\r
2135 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2136 lineGap, &logbrush, 0, NULL);
\r
2138 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2139 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2140 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2141 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2142 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2143 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2144 BOARD_WIDTH * (squareSize + lineGap);
\r
2145 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2147 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2148 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2149 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2150 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2151 lineGap / 2 + (i * (squareSize + lineGap));
\r
2152 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2153 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2154 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2158 /* [HGM] Licensing requirement */
\r
2160 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2163 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2165 GothicPopUp( "", VariantNormal);
\r
2168 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2170 /* Load piece bitmaps for this board size */
\r
2171 for (i=0; i<=2; i++) {
\r
2172 for (piece = WhitePawn;
\r
2173 (int) piece < (int) BlackPawn;
\r
2174 piece = (ChessSquare) ((int) piece + 1)) {
\r
2175 if (pieceBitmap[i][piece] != NULL)
\r
2176 DeleteObject(pieceBitmap[i][piece]);
\r
2180 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2181 // Orthodox Chess pieces
\r
2182 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2183 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2184 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2185 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2186 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2187 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2188 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2189 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2190 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2191 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2192 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2193 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2194 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2195 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2196 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2197 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2198 // in Shogi, Hijack the unused Queen for Lance
\r
2199 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2200 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2201 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2203 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2204 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2205 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2208 if(squareSize <= 72 && squareSize >= 33) {
\r
2209 /* A & C are available in most sizes now */
\r
2210 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2211 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2212 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2213 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2214 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2215 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2216 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2217 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2218 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2219 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2220 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2221 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2222 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2223 } else { // Smirf-like
\r
2224 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2225 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2226 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2228 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2229 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2232 } else { // WinBoard standard
\r
2233 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2240 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2241 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2242 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2243 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2244 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2245 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2246 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2247 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2248 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2249 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2250 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2259 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2272 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2273 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2274 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2275 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2276 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2277 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2278 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2279 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2280 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2281 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2282 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2286 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2287 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2288 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2289 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2290 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2291 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2292 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2293 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2294 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2295 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2296 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2297 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2300 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2301 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2302 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2303 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2304 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2305 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2306 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2307 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2308 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2309 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2310 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2311 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2312 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2313 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2314 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2318 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2319 /* special Shogi support in this size */
\r
2320 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2321 for (piece = WhitePawn;
\r
2322 (int) piece < (int) BlackPawn;
\r
2323 piece = (ChessSquare) ((int) piece + 1)) {
\r
2324 if (pieceBitmap[i][piece] != NULL)
\r
2325 DeleteObject(pieceBitmap[i][piece]);
\r
2328 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2330 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2331 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2332 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2333 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2334 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2335 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2336 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2337 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2342 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2344 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2345 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2346 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2347 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2348 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2349 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2350 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2351 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2356 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2358 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2359 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2360 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2361 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2362 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2363 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2364 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2365 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2375 PieceBitmap(ChessSquare p, int kind)
\r
2377 if ((int) p >= (int) BlackPawn)
\r
2378 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2380 return pieceBitmap[kind][(int) p];
\r
2383 /***************************************************************/
\r
2385 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2386 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2388 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2389 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2393 SquareToPos(int row, int column, int * x, int * y)
\r
2396 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2397 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2399 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2400 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2405 DrawCoordsOnDC(HDC hdc)
\r
2407 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
2408 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
2409 char str[2] = { NULLCHAR, NULLCHAR };
\r
2410 int oldMode, oldAlign, x, y, start, i;
\r
2414 if (!appData.showCoords)
\r
2417 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2419 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2420 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2421 oldAlign = GetTextAlign(hdc);
\r
2422 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2424 y = boardRect.top + lineGap;
\r
2425 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2427 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2428 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2429 str[0] = files[start + i];
\r
2430 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2431 y += squareSize + lineGap;
\r
2434 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2436 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2437 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2438 str[0] = ranks[start + i];
\r
2439 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2440 x += squareSize + lineGap;
\r
2443 SelectObject(hdc, oldBrush);
\r
2444 SetBkMode(hdc, oldMode);
\r
2445 SetTextAlign(hdc, oldAlign);
\r
2446 SelectObject(hdc, oldFont);
\r
2450 DrawGridOnDC(HDC hdc)
\r
2454 if (lineGap != 0) {
\r
2455 oldPen = SelectObject(hdc, gridPen);
\r
2456 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2457 SelectObject(hdc, oldPen);
\r
2461 #define HIGHLIGHT_PEN 0
\r
2462 #define PREMOVE_PEN 1
\r
2465 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2468 HPEN oldPen, hPen;
\r
2469 if (lineGap == 0) return;
\r
2471 x1 = boardRect.left +
\r
2472 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2473 y1 = boardRect.top +
\r
2474 lineGap/2 + y * (squareSize + lineGap);
\r
2476 x1 = boardRect.left +
\r
2477 lineGap/2 + x * (squareSize + lineGap);
\r
2478 y1 = boardRect.top +
\r
2479 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2481 hPen = pen ? premovePen : highlightPen;
\r
2482 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2483 MoveToEx(hdc, x1, y1, NULL);
\r
2484 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2485 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2486 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2487 LineTo(hdc, x1, y1);
\r
2488 SelectObject(hdc, oldPen);
\r
2492 DrawHighlightsOnDC(HDC hdc)
\r
2495 for (i=0; i<2; i++) {
\r
2496 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2497 DrawHighlightOnDC(hdc, TRUE,
\r
2498 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2501 for (i=0; i<2; i++) {
\r
2502 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2503 premoveHighlightInfo.sq[i].y >= 0) {
\r
2504 DrawHighlightOnDC(hdc, TRUE,
\r
2505 premoveHighlightInfo.sq[i].x,
\r
2506 premoveHighlightInfo.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 ) {
\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
2536 squareSize, squareSize,
\r
2541 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2545 squareSize, squareSize,
\r
2554 if (appData.monoMode) {
\r
2555 SelectObject(tmphdc, PieceBitmap(piece,
\r
2556 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2557 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2558 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2560 tmpSize = squareSize;
\r
2562 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2563 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2564 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2565 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2566 x += (squareSize - minorSize)>>1;
\r
2567 y += squareSize - minorSize - 2;
\r
2568 tmpSize = minorSize;
\r
2570 if (color || appData.allWhite ) {
\r
2571 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2573 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2574 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2575 if(appData.upsideDown && color==flipView)
\r
2576 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2578 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2579 /* Use black for outline of white pieces */
\r
2580 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2581 if(appData.upsideDown && color==flipView)
\r
2582 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2584 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2586 /* Use square color for details of black pieces */
\r
2587 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2588 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2589 if(appData.upsideDown && !flipView)
\r
2590 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2592 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2594 SelectObject(hdc, oldBrush);
\r
2595 SelectObject(tmphdc, oldBitmap);
\r
2599 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2600 int GetBackTextureMode( int algo )
\r
2602 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2606 case BACK_TEXTURE_MODE_PLAIN:
\r
2607 result = 1; /* Always use identity map */
\r
2609 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2610 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2618 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2619 to handle redraws cleanly (as random numbers would always be different).
\r
2621 VOID RebuildTextureSquareInfo()
\r
2631 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2633 if( liteBackTexture != NULL ) {
\r
2634 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2635 lite_w = bi.bmWidth;
\r
2636 lite_h = bi.bmHeight;
\r
2640 if( darkBackTexture != NULL ) {
\r
2641 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2642 dark_w = bi.bmWidth;
\r
2643 dark_h = bi.bmHeight;
\r
2647 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2648 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2649 if( (col + row) & 1 ) {
\r
2651 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2652 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2653 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2654 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2659 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2660 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2661 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2662 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2669 /* [AS] Arrow highlighting support */
\r
2671 static int A_WIDTH = 5; /* Width of arrow body */
\r
2673 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2674 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2676 static double Sqr( double x )
\r
2681 static int Round( double x )
\r
2683 return (int) (x + 0.5);
\r
2686 /* Draw an arrow between two points using current settings */
\r
2687 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2690 double dx, dy, j, k, x, y;
\r
2692 if( d_x == s_x ) {
\r
2693 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2695 arrow[0].x = s_x + A_WIDTH;
\r
2698 arrow[1].x = s_x + A_WIDTH;
\r
2699 arrow[1].y = d_y - h;
\r
2701 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2702 arrow[2].y = d_y - h;
\r
2707 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2708 arrow[4].y = d_y - h;
\r
2710 arrow[5].x = s_x - A_WIDTH;
\r
2711 arrow[5].y = d_y - h;
\r
2713 arrow[6].x = s_x - A_WIDTH;
\r
2716 else if( d_y == s_y ) {
\r
2717 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2720 arrow[0].y = s_y + A_WIDTH;
\r
2722 arrow[1].x = d_x - w;
\r
2723 arrow[1].y = s_y + A_WIDTH;
\r
2725 arrow[2].x = d_x - w;
\r
2726 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2731 arrow[4].x = d_x - w;
\r
2732 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2734 arrow[5].x = d_x - w;
\r
2735 arrow[5].y = s_y - A_WIDTH;
\r
2738 arrow[6].y = s_y - A_WIDTH;
\r
2741 /* [AS] Needed a lot of paper for this! :-) */
\r
2742 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2743 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2745 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2747 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2752 arrow[0].x = Round(x - j);
\r
2753 arrow[0].y = Round(y + j*dx);
\r
2755 arrow[1].x = Round(x + j);
\r
2756 arrow[1].y = Round(y - j*dx);
\r
2759 x = (double) d_x - k;
\r
2760 y = (double) d_y - k*dy;
\r
2763 x = (double) d_x + k;
\r
2764 y = (double) d_y + k*dy;
\r
2767 arrow[2].x = Round(x + j);
\r
2768 arrow[2].y = Round(y - j*dx);
\r
2770 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2771 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2776 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2777 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2779 arrow[6].x = Round(x - j);
\r
2780 arrow[6].y = Round(y + j*dx);
\r
2783 Polygon( hdc, arrow, 7 );
\r
2786 /* [AS] Draw an arrow between two squares */
\r
2787 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2789 int s_x, s_y, d_x, d_y;
\r
2796 if( s_col == d_col && s_row == d_row ) {
\r
2800 /* Get source and destination points */
\r
2801 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2802 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2805 d_y += squareSize / 4;
\r
2807 else if( d_y < s_y ) {
\r
2808 d_y += 3 * squareSize / 4;
\r
2811 d_y += squareSize / 2;
\r
2815 d_x += squareSize / 4;
\r
2817 else if( d_x < s_x ) {
\r
2818 d_x += 3 * squareSize / 4;
\r
2821 d_x += squareSize / 2;
\r
2824 s_x += squareSize / 2;
\r
2825 s_y += squareSize / 2;
\r
2827 /* Adjust width */
\r
2828 A_WIDTH = squareSize / 14;
\r
2831 stLB.lbStyle = BS_SOLID;
\r
2832 stLB.lbColor = appData.highlightArrowColor;
\r
2835 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2836 holdpen = SelectObject( hdc, hpen );
\r
2837 hbrush = CreateBrushIndirect( &stLB );
\r
2838 holdbrush = SelectObject( hdc, hbrush );
\r
2840 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2842 SelectObject( hdc, holdpen );
\r
2843 SelectObject( hdc, holdbrush );
\r
2844 DeleteObject( hpen );
\r
2845 DeleteObject( hbrush );
\r
2848 BOOL HasHighlightInfo()
\r
2850 BOOL result = FALSE;
\r
2852 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2853 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2861 BOOL IsDrawArrowEnabled()
\r
2863 BOOL result = FALSE;
\r
2865 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2872 VOID DrawArrowHighlight( HDC hdc )
\r
2874 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2875 DrawArrowBetweenSquares( hdc,
\r
2876 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2877 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2881 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2883 HRGN result = NULL;
\r
2885 if( HasHighlightInfo() ) {
\r
2886 int x1, y1, x2, y2;
\r
2887 int sx, sy, dx, dy;
\r
2889 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2890 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2892 sx = MIN( x1, x2 );
\r
2893 sy = MIN( y1, y2 );
\r
2894 dx = MAX( x1, x2 ) + squareSize;
\r
2895 dy = MAX( y1, y2 ) + squareSize;
\r
2897 result = CreateRectRgn( sx, sy, dx, dy );
\r
2904 Warning: this function modifies the behavior of several other functions.
\r
2906 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2907 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2908 repaint is scattered all over the place, which is not good for features such as
\r
2909 "arrow highlighting" that require a full repaint of the board.
\r
2911 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2912 user interaction, when speed is not so important) but especially to avoid errors
\r
2913 in the displayed graphics.
\r
2915 In such patched places, I always try refer to this function so there is a single
\r
2916 place to maintain knowledge.
\r
2918 To restore the original behavior, just return FALSE unconditionally.
\r
2920 BOOL IsFullRepaintPreferrable()
\r
2922 BOOL result = FALSE;
\r
2924 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2925 /* Arrow may appear on the board */
\r
2933 This function is called by DrawPosition to know whether a full repaint must
\r
2936 Only DrawPosition may directly call this function, which makes use of
\r
2937 some state information. Other function should call DrawPosition specifying
\r
2938 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2940 BOOL DrawPositionNeedsFullRepaint()
\r
2942 BOOL result = FALSE;
\r
2945 Probably a slightly better policy would be to trigger a full repaint
\r
2946 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2947 but animation is fast enough that it's difficult to notice.
\r
2949 if( animInfo.piece == EmptySquare ) {
\r
2950 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2959 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2961 int row, column, x, y, square_color, piece_color;
\r
2962 ChessSquare piece;
\r
2964 HDC texture_hdc = NULL;
\r
2966 /* [AS] Initialize background textures if needed */
\r
2967 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2968 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2969 if( backTextureSquareSize != squareSize
\r
2970 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2971 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2972 backTextureSquareSize = squareSize;
\r
2973 RebuildTextureSquareInfo();
\r
2976 texture_hdc = CreateCompatibleDC( hdc );
\r
2979 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2980 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2982 SquareToPos(row, column, &x, &y);
\r
2984 piece = board[row][column];
\r
2986 square_color = ((column + row) % 2) == 1;
\r
2987 if( gameInfo.variant == VariantXiangqi ) {
\r
2988 square_color = !InPalace(row, column);
\r
2989 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2990 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2992 piece_color = (int) piece < (int) BlackPawn;
\r
2995 /* [HGM] holdings file: light square or black */
\r
2996 if(column == BOARD_LEFT-2) {
\r
2997 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3000 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3004 if(column == BOARD_RGHT + 1 ) {
\r
3005 if( row < gameInfo.holdingsSize )
\r
3008 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3012 if(column == BOARD_LEFT-1 ) /* left align */
\r
3013 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3014 else if( column == BOARD_RGHT) /* right align */
\r
3015 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3017 if (appData.monoMode) {
\r
3018 if (piece == EmptySquare) {
\r
3019 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3020 square_color ? WHITENESS : BLACKNESS);
\r
3022 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3025 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3026 /* [AS] Draw the square using a texture bitmap */
\r
3027 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3028 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3029 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3032 squareSize, squareSize,
\r
3035 backTextureSquareInfo[r][c].mode,
\r
3036 backTextureSquareInfo[r][c].x,
\r
3037 backTextureSquareInfo[r][c].y );
\r
3039 SelectObject( texture_hdc, hbm );
\r
3041 if (piece != EmptySquare) {
\r
3042 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3046 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3048 oldBrush = SelectObject(hdc, brush );
\r
3049 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3050 SelectObject(hdc, oldBrush);
\r
3051 if (piece != EmptySquare)
\r
3052 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3057 if( texture_hdc != NULL ) {
\r
3058 DeleteDC( texture_hdc );
\r
3062 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3063 void fputDW(FILE *f, int x)
\r
3065 fputc(x & 255, f);
\r
3066 fputc(x>>8 & 255, f);
\r
3067 fputc(x>>16 & 255, f);
\r
3068 fputc(x>>24 & 255, f);
\r
3071 #define MAX_CLIPS 200 /* more than enough */
\r
3074 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3076 // HBITMAP bufferBitmap;
\r
3081 int w = 100, h = 50;
\r
3083 if(logo == NULL) return;
\r
3084 // GetClientRect(hwndMain, &Rect);
\r
3085 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3086 // Rect.bottom-Rect.top+1);
\r
3087 tmphdc = CreateCompatibleDC(hdc);
\r
3088 hbm = SelectObject(tmphdc, logo);
\r
3089 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3093 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3094 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3095 SelectObject(tmphdc, hbm);
\r
3099 static HDC hdcSeek;
\r
3101 // [HGM] seekgraph
\r
3102 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3105 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3106 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3107 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3108 SelectObject( hdcSeek, hp );
\r
3111 // front-end wrapper for drawing functions to do rectangles
\r
3112 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3117 if (hdcSeek == NULL) {
\r
3118 hdcSeek = GetDC(hwndMain);
\r
3119 if (!appData.monoMode) {
\r
3120 SelectPalette(hdcSeek, hPal, FALSE);
\r
3121 RealizePalette(hdcSeek);
\r
3124 hp = SelectObject( hdcSeek, gridPen );
\r
3125 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3126 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3127 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3128 SelectObject( hdcSeek, hp );
\r
3131 // front-end wrapper for putting text in graph
\r
3132 void DrawSeekText(char *buf, int x, int y)
\r
3135 SetBkMode( hdcSeek, TRANSPARENT );
\r
3136 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3137 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3140 void DrawSeekDot(int x, int y, int color)
\r
3142 int square = color & 0x80;
\r
3144 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3145 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3147 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3148 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3150 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3151 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3152 SelectObject(hdcSeek, oldBrush);
\r
3156 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3158 static Board lastReq[2], lastDrawn[2];
\r
3159 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3160 static int lastDrawnFlipView = 0;
\r
3161 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3162 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3165 HBITMAP bufferBitmap;
\r
3166 HBITMAP oldBitmap;
\r
3168 HRGN clips[MAX_CLIPS];
\r
3169 ChessSquare dragged_piece = EmptySquare;
\r
3170 int nr = twoBoards*partnerUp;
\r
3172 /* I'm undecided on this - this function figures out whether a full
\r
3173 * repaint is necessary on its own, so there's no real reason to have the
\r
3174 * caller tell it that. I think this can safely be set to FALSE - but
\r
3175 * if we trust the callers not to request full repaints unnessesarily, then
\r
3176 * we could skip some clipping work. In other words, only request a full
\r
3177 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3178 * gamestart and similar) --Hawk
\r
3180 Boolean fullrepaint = repaint;
\r
3182 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3184 if( DrawPositionNeedsFullRepaint() ) {
\r
3185 fullrepaint = TRUE;
\r
3188 if (board == NULL) {
\r
3189 if (!lastReqValid[nr]) {
\r
3192 board = lastReq[nr];
\r
3194 CopyBoard(lastReq[nr], board);
\r
3195 lastReqValid[nr] = 1;
\r
3198 if (doingSizing) {
\r
3202 if (IsIconic(hwndMain)) {
\r
3206 if (hdc == NULL) {
\r
3207 hdc = GetDC(hwndMain);
\r
3208 if (!appData.monoMode) {
\r
3209 SelectPalette(hdc, hPal, FALSE);
\r
3210 RealizePalette(hdc);
\r
3214 releaseDC = FALSE;
\r
3217 /* Create some work-DCs */
\r
3218 hdcmem = CreateCompatibleDC(hdc);
\r
3219 tmphdc = CreateCompatibleDC(hdc);
\r
3221 /* If dragging is in progress, we temporarely remove the piece */
\r
3222 /* [HGM] or temporarily decrease count if stacked */
\r
3223 /* !! Moved to before board compare !! */
\r
3224 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3225 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3226 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3227 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3228 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3230 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3231 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3232 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3234 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3237 /* Figure out which squares need updating by comparing the
\r
3238 * newest board with the last drawn board and checking if
\r
3239 * flipping has changed.
\r
3241 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3242 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3243 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3244 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3245 SquareToPos(row, column, &x, &y);
\r
3246 clips[num_clips++] =
\r
3247 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3251 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3252 for (i=0; i<2; i++) {
\r
3253 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3254 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3255 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3256 lastDrawnHighlight.sq[i].y >= 0) {
\r
3257 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3258 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3259 clips[num_clips++] =
\r
3260 CreateRectRgn(x - lineGap, y - lineGap,
\r
3261 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3263 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3264 SquareToPos(highlightInfo.sq[i].y, highlightInfo.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
3271 for (i=0; i<2; i++) {
\r
3272 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3273 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3274 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3275 lastDrawnPremove.sq[i].y >= 0) {
\r
3276 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3277 lastDrawnPremove.sq[i].x, &x, &y);
\r
3278 clips[num_clips++] =
\r
3279 CreateRectRgn(x - lineGap, y - lineGap,
\r
3280 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3282 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3283 premoveHighlightInfo.sq[i].y >= 0) {
\r
3284 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3285 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3286 clips[num_clips++] =
\r
3287 CreateRectRgn(x - lineGap, y - lineGap,
\r
3288 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3294 fullrepaint = TRUE;
\r
3297 /* Create a buffer bitmap - this is the actual bitmap
\r
3298 * being written to. When all the work is done, we can
\r
3299 * copy it to the real DC (the screen). This avoids
\r
3300 * the problems with flickering.
\r
3302 GetClientRect(hwndMain, &Rect);
\r
3303 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3304 Rect.bottom-Rect.top+1);
\r
3305 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3306 if (!appData.monoMode) {
\r
3307 SelectPalette(hdcmem, hPal, FALSE);
\r
3310 /* Create clips for dragging */
\r
3311 if (!fullrepaint) {
\r
3312 if (dragInfo.from.x >= 0) {
\r
3313 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3314 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3316 if (dragInfo.start.x >= 0) {
\r
3317 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3318 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3320 if (dragInfo.pos.x >= 0) {
\r
3321 x = dragInfo.pos.x - squareSize / 2;
\r
3322 y = dragInfo.pos.y - squareSize / 2;
\r
3323 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3325 if (dragInfo.lastpos.x >= 0) {
\r
3326 x = dragInfo.lastpos.x - squareSize / 2;
\r
3327 y = dragInfo.lastpos.y - squareSize / 2;
\r
3328 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3332 /* Are we animating a move?
\r
3334 * - remove the piece from the board (temporarely)
\r
3335 * - calculate the clipping region
\r
3337 if (!fullrepaint) {
\r
3338 if (animInfo.piece != EmptySquare) {
\r
3339 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3340 x = boardRect.left + animInfo.lastpos.x;
\r
3341 y = boardRect.top + animInfo.lastpos.y;
\r
3342 x2 = boardRect.left + animInfo.pos.x;
\r
3343 y2 = boardRect.top + animInfo.pos.y;
\r
3344 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3345 /* Slight kludge. The real problem is that after AnimateMove is
\r
3346 done, the position on the screen does not match lastDrawn.
\r
3347 This currently causes trouble only on e.p. captures in
\r
3348 atomic, where the piece moves to an empty square and then
\r
3349 explodes. The old and new positions both had an empty square
\r
3350 at the destination, but animation has drawn a piece there and
\r
3351 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3352 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3356 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3357 if (num_clips == 0)
\r
3358 fullrepaint = TRUE;
\r
3360 /* Set clipping on the memory DC */
\r
3361 if (!fullrepaint) {
\r
3362 SelectClipRgn(hdcmem, clips[0]);
\r
3363 for (x = 1; x < num_clips; x++) {
\r
3364 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3365 abort(); // this should never ever happen!
\r
3369 /* Do all the drawing to the memory DC */
\r
3370 if(explodeInfo.radius) { // [HGM] atomic
\r
3372 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3373 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3374 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3375 x += squareSize/2;
\r
3376 y += squareSize/2;
\r
3377 if(!fullrepaint) {
\r
3378 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3379 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3381 DrawGridOnDC(hdcmem);
\r
3382 DrawHighlightsOnDC(hdcmem);
\r
3383 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3384 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3385 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3386 SelectObject(hdcmem, oldBrush);
\r
3388 DrawGridOnDC(hdcmem);
\r
3389 if(nr == 0) DrawHighlightsOnDC(hdcmem); // [HGM] dual: no highlights on right board yet
\r
3390 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3392 if(nr == 0) // [HGM] dual: markers only on left board
\r
3393 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3394 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3395 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3396 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3397 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3398 SquareToPos(row, column, &x, &y);
\r
3399 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3400 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3401 SelectObject(hdcmem, oldBrush);
\r
3406 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3407 if(appData.autoLogo) {
\r
3409 switch(gameMode) { // pick logos based on game mode
\r
3410 case IcsObserving:
\r
3411 whiteLogo = second.programLogo; // ICS logo
\r
3412 blackLogo = second.programLogo;
\r
3415 case IcsPlayingWhite:
\r
3416 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3417 blackLogo = second.programLogo; // ICS logo
\r
3419 case IcsPlayingBlack:
\r
3420 whiteLogo = second.programLogo; // ICS logo
\r
3421 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3423 case TwoMachinesPlay:
\r
3424 if(first.twoMachinesColor[0] == 'b') {
\r
3425 whiteLogo = second.programLogo;
\r
3426 blackLogo = first.programLogo;
\r
3429 case MachinePlaysWhite:
\r
3430 blackLogo = userLogo;
\r
3432 case MachinePlaysBlack:
\r
3433 whiteLogo = userLogo;
\r
3434 blackLogo = first.programLogo;
\r
3437 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3438 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3441 if( appData.highlightMoveWithArrow ) {
\r
3442 DrawArrowHighlight(hdcmem);
\r
3445 DrawCoordsOnDC(hdcmem);
\r
3447 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3448 /* to make sure lastDrawn contains what is actually drawn */
\r
3450 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3451 if (dragged_piece != EmptySquare) {
\r
3452 /* [HGM] or restack */
\r
3453 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3454 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3456 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3457 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3458 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3459 x = dragInfo.pos.x - squareSize / 2;
\r
3460 y = dragInfo.pos.y - squareSize / 2;
\r
3461 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3462 ((int) dragged_piece < (int) BlackPawn),
\r
3463 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3466 /* Put the animated piece back into place and draw it */
\r
3467 if (animInfo.piece != EmptySquare) {
\r
3468 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3469 x = boardRect.left + animInfo.pos.x;
\r
3470 y = boardRect.top + animInfo.pos.y;
\r
3471 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3472 ((int) animInfo.piece < (int) BlackPawn),
\r
3473 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3476 /* Release the bufferBitmap by selecting in the old bitmap
\r
3477 * and delete the memory DC
\r
3479 SelectObject(hdcmem, oldBitmap);
\r
3482 /* Set clipping on the target DC */
\r
3483 if (!fullrepaint) {
\r
3484 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3486 GetRgnBox(clips[x], &rect);
\r
3487 DeleteObject(clips[x]);
\r
3488 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3489 rect.right + wpMain.width/2, rect.bottom);
\r
3491 SelectClipRgn(hdc, clips[0]);
\r
3492 for (x = 1; x < num_clips; x++) {
\r
3493 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3494 abort(); // this should never ever happen!
\r
3498 /* Copy the new bitmap onto the screen in one go.
\r
3499 * This way we avoid any flickering
\r
3501 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3502 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3503 boardRect.right - boardRect.left,
\r
3504 boardRect.bottom - boardRect.top,
\r
3505 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3506 if(saveDiagFlag) {
\r
3507 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3508 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3510 GetObject(bufferBitmap, sizeof(b), &b);
\r
3511 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3512 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3513 bih.biWidth = b.bmWidth;
\r
3514 bih.biHeight = b.bmHeight;
\r
3516 bih.biBitCount = b.bmBitsPixel;
\r
3517 bih.biCompression = 0;
\r
3518 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3519 bih.biXPelsPerMeter = 0;
\r
3520 bih.biYPelsPerMeter = 0;
\r
3521 bih.biClrUsed = 0;
\r
3522 bih.biClrImportant = 0;
\r
3523 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3524 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3525 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3526 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3528 wb = b.bmWidthBytes;
\r
3530 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3531 int k = ((int*) pData)[i];
\r
3532 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3533 if(j >= 16) break;
\r
3535 if(j >= nrColors) nrColors = j+1;
\r
3537 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3539 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3540 for(w=0; w<(wb>>2); w+=2) {
\r
3541 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3542 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3543 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3544 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3545 pData[p++] = m | j<<4;
\r
3547 while(p&3) pData[p++] = 0;
\r
3550 wb = ((wb+31)>>5)<<2;
\r
3552 // write BITMAPFILEHEADER
\r
3553 fprintf(diagFile, "BM");
\r
3554 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3555 fputDW(diagFile, 0);
\r
3556 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3557 // write BITMAPINFOHEADER
\r
3558 fputDW(diagFile, 40);
\r
3559 fputDW(diagFile, b.bmWidth);
\r
3560 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3561 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3562 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3563 fputDW(diagFile, 0);
\r
3564 fputDW(diagFile, 0);
\r
3565 fputDW(diagFile, 0);
\r
3566 fputDW(diagFile, 0);
\r
3567 fputDW(diagFile, 0);
\r
3568 fputDW(diagFile, 0);
\r
3569 // write color table
\r
3571 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3572 // write bitmap data
\r
3573 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3574 fputc(pData[i], diagFile);
\r
3578 SelectObject(tmphdc, oldBitmap);
\r
3580 /* Massive cleanup */
\r
3581 for (x = 0; x < num_clips; x++)
\r
3582 DeleteObject(clips[x]);
\r
3585 DeleteObject(bufferBitmap);
\r
3588 ReleaseDC(hwndMain, hdc);
\r
3590 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3592 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3594 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3597 /* CopyBoard(lastDrawn, board);*/
\r
3598 lastDrawnHighlight = highlightInfo;
\r
3599 lastDrawnPremove = premoveHighlightInfo;
\r
3600 lastDrawnFlipView = flipView;
\r
3601 lastDrawnValid[nr] = 1;
\r
3604 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3609 saveDiagFlag = 1; diagFile = f;
\r
3610 HDCDrawPosition(NULL, TRUE, NULL);
\r
3614 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3621 /*---------------------------------------------------------------------------*\
\r
3622 | CLIENT PAINT PROCEDURE
\r
3623 | This is the main event-handler for the WM_PAINT message.
\r
3625 \*---------------------------------------------------------------------------*/
\r
3627 PaintProc(HWND hwnd)
\r
3633 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3634 if (IsIconic(hwnd)) {
\r
3635 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3637 if (!appData.monoMode) {
\r
3638 SelectPalette(hdc, hPal, FALSE);
\r
3639 RealizePalette(hdc);
\r
3641 HDCDrawPosition(hdc, 1, NULL);
\r
3642 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3643 flipView = !flipView; partnerUp = !partnerUp;
\r
3644 HDCDrawPosition(hdc, 1, NULL);
\r
3645 flipView = !flipView; partnerUp = !partnerUp;
\r
3648 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3649 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3650 ETO_CLIPPED|ETO_OPAQUE,
\r
3651 &messageRect, messageText, strlen(messageText), NULL);
\r
3652 SelectObject(hdc, oldFont);
\r
3653 DisplayBothClocks();
\r
3655 EndPaint(hwnd,&ps);
\r
3663 * If the user selects on a border boundary, return -1; if off the board,
\r
3664 * return -2. Otherwise map the event coordinate to the square.
\r
3665 * The offset boardRect.left or boardRect.top must already have been
\r
3666 * subtracted from x.
\r
3668 int EventToSquare(x, limit)
\r
3676 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3678 x /= (squareSize + lineGap);
\r
3690 DropEnable dropEnables[] = {
\r
3691 { 'P', DP_Pawn, "Pawn" },
\r
3692 { 'N', DP_Knight, "Knight" },
\r
3693 { 'B', DP_Bishop, "Bishop" },
\r
3694 { 'R', DP_Rook, "Rook" },
\r
3695 { 'Q', DP_Queen, "Queen" },
\r
3699 SetupDropMenu(HMENU hmenu)
\r
3701 int i, count, enable;
\r
3703 extern char white_holding[], black_holding[];
\r
3704 char item[MSG_SIZ];
\r
3706 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3707 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3708 dropEnables[i].piece);
\r
3710 while (p && *p++ == dropEnables[i].piece) count++;
\r
3711 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3712 enable = count > 0 || !appData.testLegality
\r
3713 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3714 && !appData.icsActive);
\r
3715 ModifyMenu(hmenu, dropEnables[i].command,
\r
3716 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3717 dropEnables[i].command, item);
\r
3721 void DragPieceBegin(int x, int y)
\r
3723 dragInfo.lastpos.x = boardRect.left + x;
\r
3724 dragInfo.lastpos.y = boardRect.top + y;
\r
3725 dragInfo.from.x = fromX;
\r
3726 dragInfo.from.y = fromY;
\r
3727 dragInfo.start = dragInfo.from;
\r
3728 SetCapture(hwndMain);
\r
3731 void DragPieceEnd(int x, int y)
\r
3734 dragInfo.start.x = dragInfo.start.y = -1;
\r
3735 dragInfo.from = dragInfo.start;
\r
3736 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3739 /* Event handler for mouse messages */
\r
3741 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3745 static int recursive = 0;
\r
3747 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3750 if (message == WM_MBUTTONUP) {
\r
3751 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3752 to the middle button: we simulate pressing the left button too!
\r
3754 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3755 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3761 pt.x = LOWORD(lParam);
\r
3762 pt.y = HIWORD(lParam);
\r
3763 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3764 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3765 if (!flipView && y >= 0) {
\r
3766 y = BOARD_HEIGHT - 1 - y;
\r
3768 if (flipView && x >= 0) {
\r
3769 x = BOARD_WIDTH - 1 - x;
\r
3772 switch (message) {
\r
3773 case WM_LBUTTONDOWN:
\r
3774 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3775 if (gameMode == EditPosition) {
\r
3776 SetWhiteToPlayEvent();
\r
3777 } else if (gameMode == IcsPlayingBlack ||
\r
3778 gameMode == MachinePlaysWhite) {
\r
3780 } else if (gameMode == EditGame) {
\r
3781 AdjustClock(flipClock, -1);
\r
3783 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3784 if (gameMode == EditPosition) {
\r
3785 SetBlackToPlayEvent();
\r
3786 } else if (gameMode == IcsPlayingWhite ||
\r
3787 gameMode == MachinePlaysBlack) {
\r
3789 } else if (gameMode == EditGame) {
\r
3790 AdjustClock(!flipClock, -1);
\r
3793 dragInfo.start.x = dragInfo.start.y = -1;
\r
3794 dragInfo.from = dragInfo.start;
\r
3795 if(fromX == -1 && frozen) { // not sure where this is for
\r
3796 fromX = fromY = -1;
\r
3797 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3800 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3801 DrawPosition(TRUE, NULL);
\r
3804 case WM_LBUTTONUP:
\r
3805 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3806 DrawPosition(TRUE, NULL);
\r
3809 case WM_MOUSEMOVE:
\r
3810 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3811 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3812 if ((appData.animateDragging || appData.highlightDragging)
\r
3813 && (wParam & MK_LBUTTON)
\r
3814 && dragInfo.from.x >= 0)
\r
3816 BOOL full_repaint = FALSE;
\r
3818 if (appData.animateDragging) {
\r
3819 dragInfo.pos = pt;
\r
3821 if (appData.highlightDragging) {
\r
3822 SetHighlights(fromX, fromY, x, y);
\r
3823 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3824 full_repaint = TRUE;
\r
3828 DrawPosition( full_repaint, NULL);
\r
3830 dragInfo.lastpos = dragInfo.pos;
\r
3834 case WM_MOUSEWHEEL: // [DM]
\r
3835 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3836 /* Mouse Wheel is being rolled forward
\r
3837 * Play moves forward
\r
3839 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3840 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3841 /* Mouse Wheel is being rolled backward
\r
3842 * Play moves backward
\r
3844 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3845 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3849 case WM_MBUTTONUP:
\r
3850 case WM_RBUTTONUP:
\r
3852 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3855 case WM_MBUTTONDOWN:
\r
3856 case WM_RBUTTONDOWN:
\r
3859 fromX = fromY = -1;
\r
3860 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3861 dragInfo.start.x = dragInfo.start.y = -1;
\r
3862 dragInfo.from = dragInfo.start;
\r
3863 dragInfo.lastpos = dragInfo.pos;
\r
3864 if (appData.highlightDragging) {
\r
3865 ClearHighlights();
\r
3868 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3869 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3870 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3871 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3872 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3875 DrawPosition(TRUE, NULL);
\r
3877 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3880 if (message == WM_MBUTTONDOWN) {
\r
3881 buttonCount = 3; /* even if system didn't think so */
\r
3882 if (wParam & MK_SHIFT)
\r
3883 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3885 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3886 } else { /* message == WM_RBUTTONDOWN */
\r
3887 /* Just have one menu, on the right button. Windows users don't
\r
3888 think to try the middle one, and sometimes other software steals
\r
3889 it, or it doesn't really exist. */
\r
3890 if(gameInfo.variant != VariantShogi)
\r
3891 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3893 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3897 SetCapture(hwndMain);
3900 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3901 SetupDropMenu(hmenu);
\r
3902 MenuPopup(hwnd, pt, hmenu, -1);
\r
3912 /* Preprocess messages for buttons in main window */
\r
3914 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3916 int id = GetWindowLong(hwnd, GWL_ID);
\r
3919 for (i=0; i<N_BUTTONS; i++) {
\r
3920 if (buttonDesc[i].id == id) break;
\r
3922 if (i == N_BUTTONS) return 0;
\r
3923 switch (message) {
\r
3928 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3929 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3936 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3939 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3940 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3941 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3942 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3944 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3946 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3947 PopUpMoveDialog((char)wParam);
\r
3953 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3956 /* Process messages for Promotion dialog box */
\r
3958 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3962 switch (message) {
\r
3963 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3964 /* Center the dialog over the application window */
\r
3965 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3966 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3967 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3968 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3969 SW_SHOW : SW_HIDE);
\r
3970 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3971 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3972 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3973 PieceToChar(WhiteAngel) != '~') ||
\r
3974 (PieceToChar(BlackAngel) >= 'A' &&
\r
3975 PieceToChar(BlackAngel) != '~') ) ?
\r
3976 SW_SHOW : SW_HIDE);
\r
3977 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3978 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3979 PieceToChar(WhiteMarshall) != '~') ||
\r
3980 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3981 PieceToChar(BlackMarshall) != '~') ) ?
\r
3982 SW_SHOW : SW_HIDE);
\r
3983 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3984 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3985 gameInfo.variant != VariantShogi ?
\r
3986 SW_SHOW : SW_HIDE);
\r
3987 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3988 gameInfo.variant != VariantShogi ?
\r
3989 SW_SHOW : SW_HIDE);
\r
3990 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3991 gameInfo.variant == VariantShogi ?
\r
3992 SW_SHOW : SW_HIDE);
\r
3993 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3994 gameInfo.variant == VariantShogi ?
\r
3995 SW_SHOW : SW_HIDE);
\r
3996 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3997 gameInfo.variant == VariantSuper ?
\r
3998 SW_SHOW : SW_HIDE);
\r
4001 case WM_COMMAND: /* message: received a command */
\r
4002 switch (LOWORD(wParam)) {
\r
4004 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4005 ClearHighlights();
\r
4006 DrawPosition(FALSE, NULL);
\r
4009 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4012 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4015 promoChar = PieceToChar(BlackRook);
\r
4018 promoChar = PieceToChar(BlackBishop);
\r
4020 case PB_Chancellor:
\r
4021 promoChar = PieceToChar(BlackMarshall);
\r
4023 case PB_Archbishop:
\r
4024 promoChar = PieceToChar(BlackAngel);
\r
4027 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4032 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4033 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4034 only show the popup when we are already sure the move is valid or
\r
4035 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4036 will figure out it is a promotion from the promoChar. */
\r
4037 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4038 fromX = fromY = -1;
\r
4039 if (!appData.highlightLastMove) {
\r
4040 ClearHighlights();
\r
4041 DrawPosition(FALSE, NULL);
\r
4048 /* Pop up promotion dialog */
\r
4050 PromotionPopup(HWND hwnd)
\r
4054 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4055 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4056 hwnd, (DLGPROC)lpProc);
\r
4057 FreeProcInstance(lpProc);
\r
4063 DrawPosition(TRUE, NULL);
\r
4064 PromotionPopup(hwndMain);
\r
4067 /* Toggle ShowThinking */
\r
4069 ToggleShowThinking()
\r
4071 appData.showThinking = !appData.showThinking;
\r
4072 ShowThinkingEvent();
\r
4076 LoadGameDialog(HWND hwnd, char* title)
\r
4080 char fileTitle[MSG_SIZ];
\r
4081 f = OpenFileDialog(hwnd, "rb", "",
\r
4082 appData.oldSaveStyle ? "gam" : "pgn",
\r
4084 title, &number, fileTitle, NULL);
\r
4086 cmailMsgLoaded = FALSE;
\r
4087 if (number == 0) {
\r
4088 int error = GameListBuild(f);
\r
4090 DisplayError("Cannot build game list", error);
\r
4091 } else if (!ListEmpty(&gameList) &&
\r
4092 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4093 GameListPopUp(f, fileTitle);
\r
4096 GameListDestroy();
\r
4099 LoadGame(f, number, fileTitle, FALSE);
\r
4103 int get_term_width()
\r
4108 HFONT hfont, hold_font;
\r
4113 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4117 // get the text metrics
\r
4118 hdc = GetDC(hText);
\r
4119 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4120 if (consoleCF.dwEffects & CFE_BOLD)
\r
4121 lf.lfWeight = FW_BOLD;
\r
4122 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4123 lf.lfItalic = TRUE;
\r
4124 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4125 lf.lfStrikeOut = TRUE;
\r
4126 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4127 lf.lfUnderline = TRUE;
\r
4128 hfont = CreateFontIndirect(&lf);
\r
4129 hold_font = SelectObject(hdc, hfont);
\r
4130 GetTextMetrics(hdc, &tm);
\r
4131 SelectObject(hdc, hold_font);
\r
4132 DeleteObject(hfont);
\r
4133 ReleaseDC(hText, hdc);
\r
4135 // get the rectangle
\r
4136 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4138 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4141 void UpdateICSWidth(HWND hText)
\r
4143 LONG old_width, new_width;
\r
4145 new_width = get_term_width(hText, FALSE);
\r
4146 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4147 if (new_width != old_width)
\r
4149 ics_update_width(new_width);
\r
4150 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4155 ChangedConsoleFont()
\r
4158 CHARRANGE tmpsel, sel;
\r
4159 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4160 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4161 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4164 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4165 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4166 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4167 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4168 * size. This was undocumented in the version of MSVC++ that I had
\r
4169 * when I wrote the code, but is apparently documented now.
\r
4171 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4172 cfmt.bCharSet = f->lf.lfCharSet;
\r
4173 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4174 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4175 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4176 /* Why are the following seemingly needed too? */
\r
4177 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4178 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4179 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4181 tmpsel.cpMax = -1; /*999999?*/
\r
4182 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4183 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4184 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4185 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4187 paraf.cbSize = sizeof(paraf);
\r
4188 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4189 paraf.dxStartIndent = 0;
\r
4190 paraf.dxOffset = WRAP_INDENT;
\r
4191 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4192 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4193 UpdateICSWidth(hText);
\r
4196 /*---------------------------------------------------------------------------*\
\r
4198 * Window Proc for main window
\r
4200 \*---------------------------------------------------------------------------*/
\r
4202 /* Process messages for main window, etc. */
\r
4204 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4207 int wmId, wmEvent;
\r
4211 char fileTitle[MSG_SIZ];
\r
4212 char buf[MSG_SIZ];
\r
4213 static SnapData sd;
\r
4215 switch (message) {
\r
4217 case WM_PAINT: /* message: repaint portion of window */
\r
4221 case WM_ERASEBKGND:
\r
4222 if (IsIconic(hwnd)) {
\r
4223 /* Cheat; change the message */
\r
4224 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4226 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4230 case WM_LBUTTONDOWN:
\r
4231 case WM_MBUTTONDOWN:
\r
4232 case WM_RBUTTONDOWN:
\r
4233 case WM_LBUTTONUP:
\r
4234 case WM_MBUTTONUP:
\r
4235 case WM_RBUTTONUP:
\r
4236 case WM_MOUSEMOVE:
\r
4237 case WM_MOUSEWHEEL:
\r
4238 MouseEvent(hwnd, message, wParam, lParam);
\r
4241 JAWS_KB_NAVIGATION
\r
4245 JAWS_ALT_INTERCEPT
\r
4247 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4248 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4249 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4250 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4252 SendMessage(h, message, wParam, lParam);
\r
4253 } else if(lParam != KF_REPEAT) {
\r
4254 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4255 PopUpMoveDialog((char)wParam);
\r
4256 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4257 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4262 case WM_PALETTECHANGED:
\r
4263 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4265 HDC hdc = GetDC(hwndMain);
\r
4266 SelectPalette(hdc, hPal, TRUE);
\r
4267 nnew = RealizePalette(hdc);
\r
4269 paletteChanged = TRUE;
\r
4270 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4272 ReleaseDC(hwnd, hdc);
\r
4276 case WM_QUERYNEWPALETTE:
\r
4277 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4279 HDC hdc = GetDC(hwndMain);
\r
4280 paletteChanged = FALSE;
\r
4281 SelectPalette(hdc, hPal, FALSE);
\r
4282 nnew = RealizePalette(hdc);
\r
4284 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4286 ReleaseDC(hwnd, hdc);
\r
4291 case WM_COMMAND: /* message: command from application menu */
\r
4292 wmId = LOWORD(wParam);
\r
4293 wmEvent = HIWORD(wParam);
\r
4298 SAY("new game enter a move to play against the computer with white");
\r
4301 case IDM_NewGameFRC:
\r
4302 if( NewGameFRC() == 0 ) {
\r
4307 case IDM_NewVariant:
\r
4308 NewVariantPopup(hwnd);
\r
4311 case IDM_LoadGame:
\r
4312 LoadGameDialog(hwnd, "Load Game from File");
\r
4315 case IDM_LoadNextGame:
\r
4319 case IDM_LoadPrevGame:
\r
4323 case IDM_ReloadGame:
\r
4327 case IDM_LoadPosition:
\r
4328 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4329 Reset(FALSE, TRUE);
\r
4332 f = OpenFileDialog(hwnd, "rb", "",
\r
4333 appData.oldSaveStyle ? "pos" : "fen",
\r
4335 "Load Position from File", &number, fileTitle, NULL);
\r
4337 LoadPosition(f, number, fileTitle);
\r
4341 case IDM_LoadNextPosition:
\r
4342 ReloadPosition(1);
\r
4345 case IDM_LoadPrevPosition:
\r
4346 ReloadPosition(-1);
\r
4349 case IDM_ReloadPosition:
\r
4350 ReloadPosition(0);
\r
4353 case IDM_SaveGame:
\r
4354 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4355 f = OpenFileDialog(hwnd, "a", defName,
\r
4356 appData.oldSaveStyle ? "gam" : "pgn",
\r
4358 "Save Game to File", NULL, fileTitle, NULL);
\r
4360 SaveGame(f, 0, "");
\r
4364 case IDM_SavePosition:
\r
4365 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4366 f = OpenFileDialog(hwnd, "a", defName,
\r
4367 appData.oldSaveStyle ? "pos" : "fen",
\r
4369 "Save Position to File", NULL, fileTitle, NULL);
\r
4371 SavePosition(f, 0, "");
\r
4375 case IDM_SaveDiagram:
\r
4376 defName = "diagram";
\r
4377 f = OpenFileDialog(hwnd, "wb", defName,
\r
4380 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4386 case IDM_CopyGame:
\r
4387 CopyGameToClipboard();
\r
4390 case IDM_PasteGame:
\r
4391 PasteGameFromClipboard();
\r
4394 case IDM_CopyGameListToClipboard:
\r
4395 CopyGameListToClipboard();
\r
4398 /* [AS] Autodetect FEN or PGN data */
\r
4399 case IDM_PasteAny:
\r
4400 PasteGameOrFENFromClipboard();
\r
4403 /* [AS] Move history */
\r
4404 case IDM_ShowMoveHistory:
\r
4405 if( MoveHistoryIsUp() ) {
\r
4406 MoveHistoryPopDown();
\r
4409 MoveHistoryPopUp();
\r
4413 /* [AS] Eval graph */
\r
4414 case IDM_ShowEvalGraph:
\r
4415 if( EvalGraphIsUp() ) {
\r
4416 EvalGraphPopDown();
\r
4420 SetFocus(hwndMain);
\r
4424 /* [AS] Engine output */
\r
4425 case IDM_ShowEngineOutput:
\r
4426 if( EngineOutputIsUp() ) {
\r
4427 EngineOutputPopDown();
\r
4430 EngineOutputPopUp();
\r
4434 /* [AS] User adjudication */
\r
4435 case IDM_UserAdjudication_White:
\r
4436 UserAdjudicationEvent( +1 );
\r
4439 case IDM_UserAdjudication_Black:
\r
4440 UserAdjudicationEvent( -1 );
\r
4443 case IDM_UserAdjudication_Draw:
\r
4444 UserAdjudicationEvent( 0 );
\r
4447 /* [AS] Game list options dialog */
\r
4448 case IDM_GameListOptions:
\r
4449 GameListOptions();
\r
4456 case IDM_CopyPosition:
\r
4457 CopyFENToClipboard();
\r
4460 case IDM_PastePosition:
\r
4461 PasteFENFromClipboard();
\r
4464 case IDM_MailMove:
\r
4468 case IDM_ReloadCMailMsg:
\r
4469 Reset(TRUE, TRUE);
\r
4470 ReloadCmailMsgEvent(FALSE);
\r
4473 case IDM_Minimize:
\r
4474 ShowWindow(hwnd, SW_MINIMIZE);
\r
4481 case IDM_MachineWhite:
\r
4482 MachineWhiteEvent();
\r
4484 * refresh the tags dialog only if it's visible
\r
4486 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4488 tags = PGNTags(&gameInfo);
\r
4489 TagsPopUp(tags, CmailMsg());
\r
4492 SAY("computer starts playing white");
\r
4495 case IDM_MachineBlack:
\r
4496 MachineBlackEvent();
\r
4498 * refresh the tags dialog only if it's visible
\r
4500 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4502 tags = PGNTags(&gameInfo);
\r
4503 TagsPopUp(tags, CmailMsg());
\r
4506 SAY("computer starts playing black");
\r
4509 case IDM_TwoMachines:
\r
4510 TwoMachinesEvent();
\r
4512 * refresh the tags dialog only if it's visible
\r
4514 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4516 tags = PGNTags(&gameInfo);
\r
4517 TagsPopUp(tags, CmailMsg());
\r
4520 SAY("programs start playing each other");
\r
4523 case IDM_AnalysisMode:
\r
4524 if (!first.analysisSupport) {
\r
4525 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4526 DisplayError(buf, 0);
\r
4528 SAY("analyzing current position");
\r
4529 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4530 if (appData.icsActive) {
\r
4531 if (gameMode != IcsObserving) {
\r
4532 sprintf(buf, "You are not observing a game");
\r
4533 DisplayError(buf, 0);
\r
4534 /* secure check */
\r
4535 if (appData.icsEngineAnalyze) {
\r
4536 if (appData.debugMode)
\r
4537 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4538 ExitAnalyzeMode();
\r
4544 /* if enable, user want disable icsEngineAnalyze */
\r
4545 if (appData.icsEngineAnalyze) {
\r
4546 ExitAnalyzeMode();
\r
4550 appData.icsEngineAnalyze = TRUE;
\r
4551 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4554 if (!appData.showThinking) ToggleShowThinking();
\r
4555 AnalyzeModeEvent();
\r
4559 case IDM_AnalyzeFile:
\r
4560 if (!first.analysisSupport) {
\r
4561 char buf[MSG_SIZ];
\r
4562 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4563 DisplayError(buf, 0);
\r
4565 if (!appData.showThinking) ToggleShowThinking();
\r
4566 AnalyzeFileEvent();
\r
4567 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4568 AnalysisPeriodicEvent(1);
\r
4572 case IDM_IcsClient:
\r
4576 case IDM_EditGame:
\r
4581 case IDM_EditPosition:
\r
4582 EditPositionEvent();
\r
4583 SAY("to set up a position type a FEN");
\r
4586 case IDM_Training:
\r
4590 case IDM_ShowGameList:
\r
4591 ShowGameListProc();
\r
4594 case IDM_EditTags:
\r
4598 case IDM_EditComment:
\r
4599 if (commentUp && editComment) {
\r
4602 EditCommentEvent();
\r
4622 case IDM_CallFlag:
\r
4642 case IDM_StopObserving:
\r
4643 StopObservingEvent();
\r
4646 case IDM_StopExamining:
\r
4647 StopExaminingEvent();
\r
4651 UploadGameEvent();
\r
4654 case IDM_TypeInMove:
\r
4655 PopUpMoveDialog('\000');
\r
4658 case IDM_TypeInName:
\r
4659 PopUpNameDialog('\000');
\r
4662 case IDM_Backward:
\r
4664 SetFocus(hwndMain);
\r
4671 SetFocus(hwndMain);
\r
4676 SetFocus(hwndMain);
\r
4681 SetFocus(hwndMain);
\r
4685 RevertEvent(FALSE);
\r
4688 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4689 RevertEvent(TRUE);
\r
4692 case IDM_TruncateGame:
\r
4693 TruncateGameEvent();
\r
4700 case IDM_RetractMove:
\r
4701 RetractMoveEvent();
\r
4704 case IDM_FlipView:
\r
4705 flipView = !flipView;
\r
4706 DrawPosition(FALSE, NULL);
\r
4709 case IDM_FlipClock:
\r
4710 flipClock = !flipClock;
\r
4711 DisplayBothClocks();
\r
4712 DrawPosition(FALSE, NULL);
\r
4715 case IDM_MuteSounds:
\r
4716 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4717 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4718 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4721 case IDM_GeneralOptions:
\r
4722 GeneralOptionsPopup(hwnd);
\r
4723 DrawPosition(TRUE, NULL);
\r
4726 case IDM_BoardOptions:
\r
4727 BoardOptionsPopup(hwnd);
\r
4730 case IDM_EnginePlayOptions:
\r
4731 EnginePlayOptionsPopup(hwnd);
\r
4734 case IDM_Engine1Options:
\r
4735 EngineOptionsPopup(hwnd, &first);
\r
4738 case IDM_Engine2Options:
\r
4739 EngineOptionsPopup(hwnd, &second);
\r
4742 case IDM_OptionsUCI:
\r
4743 UciOptionsPopup(hwnd);
\r
4746 case IDM_IcsOptions:
\r
4747 IcsOptionsPopup(hwnd);
\r
4751 FontsOptionsPopup(hwnd);
\r
4755 SoundOptionsPopup(hwnd);
\r
4758 case IDM_CommPort:
\r
4759 CommPortOptionsPopup(hwnd);
\r
4762 case IDM_LoadOptions:
\r
4763 LoadOptionsPopup(hwnd);
\r
4766 case IDM_SaveOptions:
\r
4767 SaveOptionsPopup(hwnd);
\r
4770 case IDM_TimeControl:
\r
4771 TimeControlOptionsPopup(hwnd);
\r
4774 case IDM_SaveSettings:
\r
4775 SaveSettings(settingsFileName);
\r
4778 case IDM_SaveSettingsOnExit:
\r
4779 saveSettingsOnExit = !saveSettingsOnExit;
\r
4780 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4781 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4782 MF_CHECKED : MF_UNCHECKED));
\r
4793 case IDM_AboutGame:
\r
4798 appData.debugMode = !appData.debugMode;
\r
4799 if (appData.debugMode) {
\r
4800 char dir[MSG_SIZ];
\r
4801 GetCurrentDirectory(MSG_SIZ, dir);
\r
4802 SetCurrentDirectory(installDir);
\r
4803 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4804 SetCurrentDirectory(dir);
\r
4805 setbuf(debugFP, NULL);
\r
4812 case IDM_HELPCONTENTS:
\r
4813 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4814 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4815 MessageBox (GetFocus(),
\r
4816 "Unable to activate help",
\r
4817 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4821 case IDM_HELPSEARCH:
\r
4822 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4823 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4824 MessageBox (GetFocus(),
\r
4825 "Unable to activate help",
\r
4826 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4830 case IDM_HELPHELP:
\r
4831 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4832 MessageBox (GetFocus(),
\r
4833 "Unable to activate help",
\r
4834 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4839 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4841 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4842 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4843 FreeProcInstance(lpProc);
\r
4846 case IDM_DirectCommand1:
\r
4847 AskQuestionEvent("Direct Command",
\r
4848 "Send to chess program:", "", "1");
\r
4850 case IDM_DirectCommand2:
\r
4851 AskQuestionEvent("Direct Command",
\r
4852 "Send to second chess program:", "", "2");
\r
4855 case EP_WhitePawn:
\r
4856 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4857 fromX = fromY = -1;
\r
4860 case EP_WhiteKnight:
\r
4861 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4862 fromX = fromY = -1;
\r
4865 case EP_WhiteBishop:
\r
4866 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4867 fromX = fromY = -1;
\r
4870 case EP_WhiteRook:
\r
4871 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4872 fromX = fromY = -1;
\r
4875 case EP_WhiteQueen:
\r
4876 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4877 fromX = fromY = -1;
\r
4880 case EP_WhiteFerz:
\r
4881 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4882 fromX = fromY = -1;
\r
4885 case EP_WhiteWazir:
\r
4886 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4887 fromX = fromY = -1;
\r
4890 case EP_WhiteAlfil:
\r
4891 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4892 fromX = fromY = -1;
\r
4895 case EP_WhiteCannon:
\r
4896 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4897 fromX = fromY = -1;
\r
4900 case EP_WhiteCardinal:
\r
4901 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4902 fromX = fromY = -1;
\r
4905 case EP_WhiteMarshall:
\r
4906 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4907 fromX = fromY = -1;
\r
4910 case EP_WhiteKing:
\r
4911 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4912 fromX = fromY = -1;
\r
4915 case EP_BlackPawn:
\r
4916 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4917 fromX = fromY = -1;
\r
4920 case EP_BlackKnight:
\r
4921 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4922 fromX = fromY = -1;
\r
4925 case EP_BlackBishop:
\r
4926 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4927 fromX = fromY = -1;
\r
4930 case EP_BlackRook:
\r
4931 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4932 fromX = fromY = -1;
\r
4935 case EP_BlackQueen:
\r
4936 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4937 fromX = fromY = -1;
\r
4940 case EP_BlackFerz:
\r
4941 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4942 fromX = fromY = -1;
\r
4945 case EP_BlackWazir:
\r
4946 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4947 fromX = fromY = -1;
\r
4950 case EP_BlackAlfil:
\r
4951 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4952 fromX = fromY = -1;
\r
4955 case EP_BlackCannon:
\r
4956 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4957 fromX = fromY = -1;
\r
4960 case EP_BlackCardinal:
\r
4961 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4962 fromX = fromY = -1;
\r
4965 case EP_BlackMarshall:
\r
4966 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4967 fromX = fromY = -1;
\r
4970 case EP_BlackKing:
\r
4971 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4972 fromX = fromY = -1;
\r
4975 case EP_EmptySquare:
\r
4976 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4977 fromX = fromY = -1;
\r
4980 case EP_ClearBoard:
\r
4981 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4982 fromX = fromY = -1;
\r
4986 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4987 fromX = fromY = -1;
\r
4991 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4992 fromX = fromY = -1;
\r
4996 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4997 fromX = fromY = -1;
\r
5001 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5002 fromX = fromY = -1;
\r
5006 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5007 fromX = fromY = -1;
\r
5011 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5012 fromX = fromY = -1;
\r
5016 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5017 fromX = fromY = -1;
\r
5021 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5022 fromX = fromY = -1;
\r
5026 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5027 fromX = fromY = -1;
\r
5031 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5037 case CLOCK_TIMER_ID:
\r
5038 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5039 clockTimerEvent = 0;
\r
5040 DecrementClocks(); /* call into back end */
\r
5042 case LOAD_GAME_TIMER_ID:
\r
5043 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5044 loadGameTimerEvent = 0;
\r
5045 AutoPlayGameLoop(); /* call into back end */
\r
5047 case ANALYSIS_TIMER_ID:
\r
5048 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5049 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5050 AnalysisPeriodicEvent(0);
\r
5052 KillTimer(hwnd, analysisTimerEvent);
\r
5053 analysisTimerEvent = 0;
\r
5056 case DELAYED_TIMER_ID:
\r
5057 KillTimer(hwnd, delayedTimerEvent);
\r
5058 delayedTimerEvent = 0;
\r
5059 delayedTimerCallback();
\r
5064 case WM_USER_Input:
\r
5065 InputEvent(hwnd, message, wParam, lParam);
\r
5068 /* [AS] Also move "attached" child windows */
\r
5069 case WM_WINDOWPOSCHANGING:
\r
5071 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5072 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5074 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5075 /* Window is moving */
\r
5078 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5079 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5080 rcMain.right = wpMain.x + wpMain.width;
\r
5081 rcMain.top = wpMain.y;
\r
5082 rcMain.bottom = wpMain.y + wpMain.height;
\r
5084 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5085 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5086 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5087 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5088 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5089 wpMain.x = lpwp->x;
\r
5090 wpMain.y = lpwp->y;
\r
5095 /* [AS] Snapping */
\r
5096 case WM_ENTERSIZEMOVE:
\r
5097 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5098 if (hwnd == hwndMain) {
\r
5099 doingSizing = TRUE;
\r
5102 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5106 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5107 if (hwnd == hwndMain) {
\r
5108 lastSizing = wParam;
\r
5113 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5114 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5116 case WM_EXITSIZEMOVE:
\r
5117 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5118 if (hwnd == hwndMain) {
\r
5120 doingSizing = FALSE;
\r
5121 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5122 GetClientRect(hwnd, &client);
\r
5123 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5125 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5127 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5130 case WM_DESTROY: /* message: window being destroyed */
\r
5131 PostQuitMessage(0);
\r
5135 if (hwnd == hwndMain) {
\r
5140 default: /* Passes it on if unprocessed */
\r
5141 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5146 /*---------------------------------------------------------------------------*\
\r
5148 * Misc utility routines
\r
5150 \*---------------------------------------------------------------------------*/
\r
5153 * Decent random number generator, at least not as bad as Windows
\r
5154 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5156 unsigned int randstate;
\r
5161 randstate = randstate * 1664525 + 1013904223;
\r
5162 return (int) randstate & 0x7fffffff;
\r
5166 mysrandom(unsigned int seed)
\r
5173 * returns TRUE if user selects a different color, FALSE otherwise
\r
5177 ChangeColor(HWND hwnd, COLORREF *which)
\r
5179 static BOOL firstTime = TRUE;
\r
5180 static DWORD customColors[16];
\r
5182 COLORREF newcolor;
\r
5187 /* Make initial colors in use available as custom colors */
\r
5188 /* Should we put the compiled-in defaults here instead? */
\r
5190 customColors[i++] = lightSquareColor & 0xffffff;
\r
5191 customColors[i++] = darkSquareColor & 0xffffff;
\r
5192 customColors[i++] = whitePieceColor & 0xffffff;
\r
5193 customColors[i++] = blackPieceColor & 0xffffff;
\r
5194 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5195 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5197 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5198 customColors[i++] = textAttribs[ccl].color;
\r
5200 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5201 firstTime = FALSE;
\r
5204 cc.lStructSize = sizeof(cc);
\r
5205 cc.hwndOwner = hwnd;
\r
5206 cc.hInstance = NULL;
\r
5207 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5208 cc.lpCustColors = (LPDWORD) customColors;
\r
5209 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5211 if (!ChooseColor(&cc)) return FALSE;
\r
5213 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5214 if (newcolor == *which) return FALSE;
\r
5215 *which = newcolor;
\r
5219 InitDrawingColors();
\r
5220 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5225 MyLoadSound(MySound *ms)
\r
5231 if (ms->data) free(ms->data);
\r
5234 switch (ms->name[0]) {
\r
5240 /* System sound from Control Panel. Don't preload here. */
\r
5244 if (ms->name[1] == NULLCHAR) {
\r
5245 /* "!" alone = silence */
\r
5248 /* Builtin wave resource. Error if not found. */
\r
5249 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5250 if (h == NULL) break;
\r
5251 ms->data = (void *)LoadResource(hInst, h);
\r
5252 if (h == NULL) break;
\r
5257 /* .wav file. Error if not found. */
\r
5258 f = fopen(ms->name, "rb");
\r
5259 if (f == NULL) break;
\r
5260 if (fstat(fileno(f), &st) < 0) break;
\r
5261 ms->data = malloc(st.st_size);
\r
5262 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5268 char buf[MSG_SIZ];
\r
5269 sprintf(buf, "Error loading sound %s", ms->name);
\r
5270 DisplayError(buf, GetLastError());
\r
5276 MyPlaySound(MySound *ms)
\r
5278 BOOLEAN ok = FALSE;
\r
5280 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5281 switch (ms->name[0]) {
\r
5283 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5288 /* System sound from Control Panel (deprecated feature).
\r
5289 "$" alone or an unset sound name gets default beep (still in use). */
\r
5290 if (ms->name[1]) {
\r
5291 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5293 if (!ok) ok = MessageBeep(MB_OK);
\r
5296 /* Builtin wave resource, or "!" alone for silence */
\r
5297 if (ms->name[1]) {
\r
5298 if (ms->data == NULL) return FALSE;
\r
5299 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5305 /* .wav file. Error if not found. */
\r
5306 if (ms->data == NULL) return FALSE;
\r
5307 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5310 /* Don't print an error: this can happen innocently if the sound driver
\r
5311 is busy; for instance, if another instance of WinBoard is playing
\r
5312 a sound at about the same time. */
\r
5318 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5321 OPENFILENAME *ofn;
\r
5322 static UINT *number; /* gross that this is static */
\r
5324 switch (message) {
\r
5325 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5326 /* Center the dialog over the application window */
\r
5327 ofn = (OPENFILENAME *) lParam;
\r
5328 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5329 number = (UINT *) ofn->lCustData;
\r
5330 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5334 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5335 return FALSE; /* Allow for further processing */
\r
5338 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5339 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5341 return FALSE; /* Allow for further processing */
\r
5347 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5349 static UINT *number;
\r
5350 OPENFILENAME *ofname;
\r
5353 case WM_INITDIALOG:
\r
5354 ofname = (OPENFILENAME *)lParam;
\r
5355 number = (UINT *)(ofname->lCustData);
\r
5358 ofnot = (OFNOTIFY *)lParam;
\r
5359 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5360 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5369 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5370 char *nameFilt, char *dlgTitle, UINT *number,
\r
5371 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5373 OPENFILENAME openFileName;
\r
5374 char buf1[MSG_SIZ];
\r
5377 if (fileName == NULL) fileName = buf1;
\r
5378 if (defName == NULL) {
\r
5379 strcpy(fileName, "*.");
\r
5380 strcat(fileName, defExt);
\r
5382 strcpy(fileName, defName);
\r
5384 if (fileTitle) strcpy(fileTitle, "");
\r
5385 if (number) *number = 0;
\r
5387 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5388 openFileName.hwndOwner = hwnd;
\r
5389 openFileName.hInstance = (HANDLE) hInst;
\r
5390 openFileName.lpstrFilter = nameFilt;
\r
5391 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5392 openFileName.nMaxCustFilter = 0L;
\r
5393 openFileName.nFilterIndex = 1L;
\r
5394 openFileName.lpstrFile = fileName;
\r
5395 openFileName.nMaxFile = MSG_SIZ;
\r
5396 openFileName.lpstrFileTitle = fileTitle;
\r
5397 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5398 openFileName.lpstrInitialDir = NULL;
\r
5399 openFileName.lpstrTitle = dlgTitle;
\r
5400 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5401 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5402 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5403 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5404 openFileName.nFileOffset = 0;
\r
5405 openFileName.nFileExtension = 0;
\r
5406 openFileName.lpstrDefExt = defExt;
\r
5407 openFileName.lCustData = (LONG) number;
\r
5408 openFileName.lpfnHook = oldDialog ?
\r
5409 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5410 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5412 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5413 GetOpenFileName(&openFileName)) {
\r
5414 /* open the file */
\r
5415 f = fopen(openFileName.lpstrFile, write);
\r
5417 MessageBox(hwnd, "File open failed", NULL,
\r
5418 MB_OK|MB_ICONEXCLAMATION);
\r
5422 int err = CommDlgExtendedError();
\r
5423 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5432 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5434 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5437 * Get the first pop-up menu in the menu template. This is the
\r
5438 * menu that TrackPopupMenu displays.
\r
5440 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5442 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5445 * TrackPopup uses screen coordinates, so convert the
\r
5446 * coordinates of the mouse click to screen coordinates.
\r
5448 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5450 /* Draw and track the floating pop-up menu. */
\r
5451 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5452 pt.x, pt.y, 0, hwnd, NULL);
\r
5454 /* Destroy the menu.*/
\r
5455 DestroyMenu(hmenu);
\r
5460 int sizeX, sizeY, newSizeX, newSizeY;
\r
5462 } ResizeEditPlusButtonsClosure;
\r
5465 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5467 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5471 if (hChild == cl->hText) return TRUE;
\r
5472 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5473 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5474 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5475 ScreenToClient(cl->hDlg, &pt);
\r
5476 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5477 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5481 /* Resize a dialog that has a (rich) edit field filling most of
\r
5482 the top, with a row of buttons below */
\r
5484 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5487 int newTextHeight, newTextWidth;
\r
5488 ResizeEditPlusButtonsClosure cl;
\r
5490 /*if (IsIconic(hDlg)) return;*/
\r
5491 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5493 cl.hdwp = BeginDeferWindowPos(8);
\r
5495 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5496 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5497 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5498 if (newTextHeight < 0) {
\r
5499 newSizeY += -newTextHeight;
\r
5500 newTextHeight = 0;
\r
5502 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5503 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5509 cl.newSizeX = newSizeX;
\r
5510 cl.newSizeY = newSizeY;
\r
5511 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5513 EndDeferWindowPos(cl.hdwp);
\r
5516 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5518 RECT rChild, rParent;
\r
5519 int wChild, hChild, wParent, hParent;
\r
5520 int wScreen, hScreen, xNew, yNew;
\r
5523 /* Get the Height and Width of the child window */
\r
5524 GetWindowRect (hwndChild, &rChild);
\r
5525 wChild = rChild.right - rChild.left;
\r
5526 hChild = rChild.bottom - rChild.top;
\r
5528 /* Get the Height and Width of the parent window */
\r
5529 GetWindowRect (hwndParent, &rParent);
\r
5530 wParent = rParent.right - rParent.left;
\r
5531 hParent = rParent.bottom - rParent.top;
\r
5533 /* Get the display limits */
\r
5534 hdc = GetDC (hwndChild);
\r
5535 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5536 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5537 ReleaseDC(hwndChild, hdc);
\r
5539 /* Calculate new X position, then adjust for screen */
\r
5540 xNew = rParent.left + ((wParent - wChild) /2);
\r
5543 } else if ((xNew+wChild) > wScreen) {
\r
5544 xNew = wScreen - wChild;
\r
5547 /* Calculate new Y position, then adjust for screen */
\r
5549 yNew = rParent.top + ((hParent - hChild) /2);
\r
5552 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5557 } else if ((yNew+hChild) > hScreen) {
\r
5558 yNew = hScreen - hChild;
\r
5561 /* Set it, and return */
\r
5562 return SetWindowPos (hwndChild, NULL,
\r
5563 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5566 /* Center one window over another */
\r
5567 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5569 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5572 /*---------------------------------------------------------------------------*\
\r
5574 * Startup Dialog functions
\r
5576 \*---------------------------------------------------------------------------*/
\r
5578 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5580 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5582 while (*cd != NULL) {
\r
5583 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5589 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5591 char buf1[MAX_ARG_LEN];
\r
5594 if (str[0] == '@') {
\r
5595 FILE* f = fopen(str + 1, "r");
\r
5597 DisplayFatalError(str + 1, errno, 2);
\r
5600 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5602 buf1[len] = NULLCHAR;
\r
5606 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5609 char buf[MSG_SIZ];
\r
5610 char *end = strchr(str, '\n');
\r
5611 if (end == NULL) return;
\r
5612 memcpy(buf, str, end - str);
\r
5613 buf[end - str] = NULLCHAR;
\r
5614 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5620 SetStartupDialogEnables(HWND hDlg)
\r
5622 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5623 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5624 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5625 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5626 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5627 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5628 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5629 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5630 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5631 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5632 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5633 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5634 IsDlgButtonChecked(hDlg, OPT_View));
\r
5638 QuoteForFilename(char *filename)
\r
5640 int dquote, space;
\r
5641 dquote = strchr(filename, '"') != NULL;
\r
5642 space = strchr(filename, ' ') != NULL;
\r
5643 if (dquote || space) {
\r
5655 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5657 char buf[MSG_SIZ];
\r
5660 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5661 q = QuoteForFilename(nthcp);
\r
5662 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5663 if (*nthdir != NULLCHAR) {
\r
5664 q = QuoteForFilename(nthdir);
\r
5665 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5667 if (*nthcp == NULLCHAR) {
\r
5668 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5669 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5670 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5671 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5676 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5678 char buf[MSG_SIZ];
\r
5682 switch (message) {
\r
5683 case WM_INITDIALOG:
\r
5684 /* Center the dialog */
\r
5685 CenterWindow (hDlg, GetDesktopWindow());
\r
5686 /* Initialize the dialog items */
\r
5687 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5688 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5689 firstChessProgramNames);
\r
5690 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5691 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5692 secondChessProgramNames);
\r
5693 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5694 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5695 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5696 if (*appData.icsHelper != NULLCHAR) {
\r
5697 char *q = QuoteForFilename(appData.icsHelper);
\r
5698 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5700 if (*appData.icsHost == NULLCHAR) {
\r
5701 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5702 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5703 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5704 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5705 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5708 if (appData.icsActive) {
\r
5709 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5711 else if (appData.noChessProgram) {
\r
5712 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5715 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5718 SetStartupDialogEnables(hDlg);
\r
5722 switch (LOWORD(wParam)) {
\r
5724 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5725 strcpy(buf, "/fcp=");
\r
5726 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5728 ParseArgs(StringGet, &p);
\r
5729 strcpy(buf, "/scp=");
\r
5730 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5732 ParseArgs(StringGet, &p);
\r
5733 appData.noChessProgram = FALSE;
\r
5734 appData.icsActive = FALSE;
\r
5735 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5736 strcpy(buf, "/ics /icshost=");
\r
5737 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5739 ParseArgs(StringGet, &p);
\r
5740 if (appData.zippyPlay) {
\r
5741 strcpy(buf, "/fcp=");
\r
5742 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5744 ParseArgs(StringGet, &p);
\r
5746 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5747 appData.noChessProgram = TRUE;
\r
5748 appData.icsActive = FALSE;
\r
5750 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5751 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5754 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5755 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5757 ParseArgs(StringGet, &p);
\r
5759 EndDialog(hDlg, TRUE);
\r
5766 case IDM_HELPCONTENTS:
\r
5767 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5768 MessageBox (GetFocus(),
\r
5769 "Unable to activate help",
\r
5770 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5775 SetStartupDialogEnables(hDlg);
\r
5783 /*---------------------------------------------------------------------------*\
\r
5785 * About box dialog functions
\r
5787 \*---------------------------------------------------------------------------*/
\r
5789 /* Process messages for "About" dialog box */
\r
5791 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5793 switch (message) {
\r
5794 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5795 /* Center the dialog over the application window */
\r
5796 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5797 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5801 case WM_COMMAND: /* message: received a command */
\r
5802 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5803 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5804 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5812 /*---------------------------------------------------------------------------*\
\r
5814 * Comment Dialog functions
\r
5816 \*---------------------------------------------------------------------------*/
\r
5819 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5821 static HANDLE hwndText = NULL;
\r
5822 int len, newSizeX, newSizeY, flags;
\r
5823 static int sizeX, sizeY;
\r
5828 switch (message) {
\r
5829 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5830 /* Initialize the dialog items */
\r
5831 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5832 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5833 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5834 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5835 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5836 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5837 SetWindowText(hDlg, commentTitle);
\r
5838 if (editComment) {
\r
5839 SetFocus(hwndText);
\r
5841 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5843 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5844 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5845 MAKELPARAM(FALSE, 0));
\r
5846 /* Size and position the dialog */
\r
5847 if (!commentDialog) {
\r
5848 commentDialog = hDlg;
\r
5849 flags = SWP_NOZORDER;
\r
5850 GetClientRect(hDlg, &rect);
\r
5851 sizeX = rect.right;
\r
5852 sizeY = rect.bottom;
\r
5853 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5854 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5855 WINDOWPLACEMENT wp;
\r
5856 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5857 wp.length = sizeof(WINDOWPLACEMENT);
\r
5859 wp.showCmd = SW_SHOW;
\r
5860 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5861 wp.rcNormalPosition.left = wpComment.x;
\r
5862 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5863 wp.rcNormalPosition.top = wpComment.y;
\r
5864 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5865 SetWindowPlacement(hDlg, &wp);
\r
5867 GetClientRect(hDlg, &rect);
\r
5868 newSizeX = rect.right;
\r
5869 newSizeY = rect.bottom;
\r
5870 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5871 newSizeX, newSizeY);
\r
5876 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5879 case WM_COMMAND: /* message: received a command */
\r
5880 switch (LOWORD(wParam)) {
\r
5882 if (editComment) {
\r
5884 /* Read changed options from the dialog box */
\r
5885 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5886 len = GetWindowTextLength(hwndText);
\r
5887 str = (char *) malloc(len + 1);
\r
5888 GetWindowText(hwndText, str, len + 1);
\r
5897 ReplaceComment(commentIndex, str);
\r
5904 case OPT_CancelComment:
\r
5908 case OPT_ClearComment:
\r
5909 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5912 case OPT_EditComment:
\r
5913 EditCommentEvent();
\r
5921 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5922 if( wParam == OPT_CommentText ) {
\r
5923 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5925 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5929 pt.x = LOWORD( lpMF->lParam );
\r
5930 pt.y = HIWORD( lpMF->lParam );
\r
5932 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5934 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5935 len = GetWindowTextLength(hwndText);
\r
5936 str = (char *) malloc(len + 1);
\r
5937 GetWindowText(hwndText, str, len + 1);
\r
5938 ReplaceComment(commentIndex, str);
\r
5939 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5940 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5943 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5944 lpMF->msg = WM_USER;
\r
5952 newSizeX = LOWORD(lParam);
\r
5953 newSizeY = HIWORD(lParam);
\r
5954 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5959 case WM_GETMINMAXINFO:
\r
5960 /* Prevent resizing window too small */
\r
5961 mmi = (MINMAXINFO *) lParam;
\r
5962 mmi->ptMinTrackSize.x = 100;
\r
5963 mmi->ptMinTrackSize.y = 100;
\r
5970 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5975 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5977 if (str == NULL) str = "";
\r
5978 p = (char *) malloc(2 * strlen(str) + 2);
\r
5981 if (*str == '\n') *q++ = '\r';
\r
5985 if (commentText != NULL) free(commentText);
\r
5987 commentIndex = index;
\r
5988 commentTitle = title;
\r
5990 editComment = edit;
\r
5992 if (commentDialog) {
\r
5993 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5994 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5996 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5997 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5998 hwndMain, (DLGPROC)lpProc);
\r
5999 FreeProcInstance(lpProc);
\r
6005 /*---------------------------------------------------------------------------*\
\r
6007 * Type-in move dialog functions
\r
6009 \*---------------------------------------------------------------------------*/
\r
6012 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6014 char move[MSG_SIZ];
\r
6016 ChessMove moveType;
\r
6017 int fromX, fromY, toX, toY;
\r
6020 switch (message) {
\r
6021 case WM_INITDIALOG:
\r
6022 move[0] = (char) lParam;
\r
6023 move[1] = NULLCHAR;
\r
6024 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6025 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6026 SetWindowText(hInput, move);
\r
6028 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6032 switch (LOWORD(wParam)) {
\r
6034 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6035 { int n; Board board;
\r
6037 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6038 EditPositionPasteFEN(move);
\r
6039 EndDialog(hDlg, TRUE);
\r
6042 // [HGM] movenum: allow move number to be typed in any mode
\r
6043 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6045 EndDialog(hDlg, TRUE);
\r
6049 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6050 gameMode != Training) {
\r
6051 DisplayMoveError("Displayed move is not current");
\r
6053 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6054 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6055 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6056 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6057 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6058 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6059 if (gameMode != Training)
\r
6060 forwardMostMove = currentMove;
\r
6061 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6063 DisplayMoveError("Could not parse move");
\r
6066 EndDialog(hDlg, TRUE);
\r
6069 EndDialog(hDlg, FALSE);
\r
6080 PopUpMoveDialog(char firstchar)
\r
6084 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6085 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6086 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6087 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6088 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6089 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6090 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6091 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6092 gameMode == Training) {
\r
6093 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6094 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6095 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6096 FreeProcInstance(lpProc);
\r
6100 /*---------------------------------------------------------------------------*\
\r
6102 * Type-in name dialog functions
\r
6104 \*---------------------------------------------------------------------------*/
\r
6107 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6109 char move[MSG_SIZ];
\r
6112 switch (message) {
\r
6113 case WM_INITDIALOG:
\r
6114 move[0] = (char) lParam;
\r
6115 move[1] = NULLCHAR;
\r
6116 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6117 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6118 SetWindowText(hInput, move);
\r
6120 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6124 switch (LOWORD(wParam)) {
\r
6126 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6127 appData.userName = strdup(move);
\r
6130 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6131 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6132 DisplayTitle(move);
\r
6136 EndDialog(hDlg, TRUE);
\r
6139 EndDialog(hDlg, FALSE);
\r
6150 PopUpNameDialog(char firstchar)
\r
6154 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6155 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6156 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6157 FreeProcInstance(lpProc);
\r
6160 /*---------------------------------------------------------------------------*\
\r
6164 \*---------------------------------------------------------------------------*/
\r
6166 /* Nonmodal error box */
\r
6167 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6168 WPARAM wParam, LPARAM lParam);
\r
6171 ErrorPopUp(char *title, char *content)
\r
6175 BOOLEAN modal = hwndMain == NULL;
\r
6193 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6194 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6197 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6199 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6200 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6201 hwndMain, (DLGPROC)lpProc);
\r
6202 FreeProcInstance(lpProc);
\r
6209 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6210 if (errorDialog == NULL) return;
\r
6211 DestroyWindow(errorDialog);
\r
6212 errorDialog = NULL;
\r
6213 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6217 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6222 switch (message) {
\r
6223 case WM_INITDIALOG:
\r
6224 GetWindowRect(hDlg, &rChild);
\r
6227 SetWindowPos(hDlg, NULL, rChild.left,
\r
6228 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6229 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6233 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6234 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6235 and it doesn't work when you resize the dialog.
\r
6236 For now, just give it a default position.
\r
6238 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6240 errorDialog = hDlg;
\r
6241 SetWindowText(hDlg, errorTitle);
\r
6242 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6243 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6247 switch (LOWORD(wParam)) {
\r
6250 if (errorDialog == hDlg) errorDialog = NULL;
\r
6251 DestroyWindow(hDlg);
\r
6263 HWND gothicDialog = NULL;
\r
6266 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6270 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6272 switch (message) {
\r
6273 case WM_INITDIALOG:
\r
6274 GetWindowRect(hDlg, &rChild);
\r
6276 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6280 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6281 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6282 and it doesn't work when you resize the dialog.
\r
6283 For now, just give it a default position.
\r
6285 gothicDialog = hDlg;
\r
6286 SetWindowText(hDlg, errorTitle);
\r
6287 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6288 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6292 switch (LOWORD(wParam)) {
\r
6295 if (errorDialog == hDlg) errorDialog = NULL;
\r
6296 DestroyWindow(hDlg);
\r
6308 GothicPopUp(char *title, VariantClass variant)
\r
6311 static char *lastTitle;
\r
6313 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6314 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6316 if(lastTitle != title && gothicDialog != NULL) {
\r
6317 DestroyWindow(gothicDialog);
\r
6318 gothicDialog = NULL;
\r
6320 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6321 title = lastTitle;
\r
6322 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6323 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6324 hwndMain, (DLGPROC)lpProc);
\r
6325 FreeProcInstance(lpProc);
\r
6330 /*---------------------------------------------------------------------------*\
\r
6332 * Ics Interaction console functions
\r
6334 \*---------------------------------------------------------------------------*/
\r
6336 #define HISTORY_SIZE 64
\r
6337 static char *history[HISTORY_SIZE];
\r
6338 int histIn = 0, histP = 0;
\r
6341 SaveInHistory(char *cmd)
\r
6343 if (history[histIn] != NULL) {
\r
6344 free(history[histIn]);
\r
6345 history[histIn] = NULL;
\r
6347 if (*cmd == NULLCHAR) return;
\r
6348 history[histIn] = StrSave(cmd);
\r
6349 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6350 if (history[histIn] != NULL) {
\r
6351 free(history[histIn]);
\r
6352 history[histIn] = NULL;
\r
6358 PrevInHistory(char *cmd)
\r
6361 if (histP == histIn) {
\r
6362 if (history[histIn] != NULL) free(history[histIn]);
\r
6363 history[histIn] = StrSave(cmd);
\r
6365 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6366 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6368 return history[histP];
\r
6374 if (histP == histIn) return NULL;
\r
6375 histP = (histP + 1) % HISTORY_SIZE;
\r
6376 return history[histP];
\r
6380 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6384 hmenu = LoadMenu(hInst, "TextMenu");
\r
6385 h = GetSubMenu(hmenu, 0);
\r
6387 if (strcmp(e->item, "-") == 0) {
\r
6388 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6389 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6390 int flags = MF_STRING, j = 0;
\r
6391 if (e->item[0] == '|') {
\r
6392 flags |= MF_MENUBARBREAK;
\r
6395 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6396 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6404 WNDPROC consoleTextWindowProc;
\r
6407 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6409 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6410 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6414 SetWindowText(hInput, command);
\r
6416 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6418 sel.cpMin = 999999;
\r
6419 sel.cpMax = 999999;
\r
6420 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6425 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6426 if (sel.cpMin == sel.cpMax) {
\r
6427 /* Expand to surrounding word */
\r
6430 tr.chrg.cpMax = sel.cpMin;
\r
6431 tr.chrg.cpMin = --sel.cpMin;
\r
6432 if (sel.cpMin < 0) break;
\r
6433 tr.lpstrText = name;
\r
6434 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6435 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6439 tr.chrg.cpMin = sel.cpMax;
\r
6440 tr.chrg.cpMax = ++sel.cpMax;
\r
6441 tr.lpstrText = name;
\r
6442 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6443 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6446 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6447 MessageBeep(MB_ICONEXCLAMATION);
\r
6451 tr.lpstrText = name;
\r
6452 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6454 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6455 MessageBeep(MB_ICONEXCLAMATION);
\r
6458 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6461 sprintf(buf, "%s %s", command, name);
\r
6462 SetWindowText(hInput, buf);
\r
6463 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6465 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6466 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6467 SetWindowText(hInput, buf);
\r
6468 sel.cpMin = 999999;
\r
6469 sel.cpMax = 999999;
\r
6470 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6476 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6481 switch (message) {
\r
6483 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6486 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6489 sel.cpMin = 999999;
\r
6490 sel.cpMax = 999999;
\r
6491 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6492 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6497 if(wParam != '\022') {
\r
6498 if (wParam == '\t') {
\r
6499 if (GetKeyState(VK_SHIFT) < 0) {
\r
6501 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6502 if (buttonDesc[0].hwnd) {
\r
6503 SetFocus(buttonDesc[0].hwnd);
\r
6505 SetFocus(hwndMain);
\r
6509 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6512 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6513 JAWS_DELETE( SetFocus(hInput); )
\r
6514 SendMessage(hInput, message, wParam, lParam);
\r
6517 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6518 case WM_RBUTTONDOWN:
\r
6519 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6520 /* Move selection here if it was empty */
\r
6522 pt.x = LOWORD(lParam);
\r
6523 pt.y = HIWORD(lParam);
\r
6524 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6525 if (sel.cpMin == sel.cpMax) {
\r
6526 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6527 sel.cpMax = sel.cpMin;
\r
6528 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6530 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6531 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6533 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6534 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6535 if (sel.cpMin == sel.cpMax) {
\r
6536 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6537 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6539 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6540 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6542 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6543 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6544 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6545 MenuPopup(hwnd, pt, hmenu, -1);
\r
6549 case WM_RBUTTONUP:
\r
6550 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6551 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6552 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6556 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6558 return SendMessage(hInput, message, wParam, lParam);
\r
6559 case WM_MBUTTONDOWN:
\r
6560 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6562 switch (LOWORD(wParam)) {
\r
6563 case IDM_QuickPaste:
\r
6565 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6566 if (sel.cpMin == sel.cpMax) {
\r
6567 MessageBeep(MB_ICONEXCLAMATION);
\r
6570 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6571 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6572 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6577 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6580 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6583 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6587 int i = LOWORD(wParam) - IDM_CommandX;
\r
6588 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6589 icsTextMenuEntry[i].command != NULL) {
\r
6590 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6591 icsTextMenuEntry[i].getname,
\r
6592 icsTextMenuEntry[i].immediate);
\r
6600 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6603 WNDPROC consoleInputWindowProc;
\r
6606 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6608 char buf[MSG_SIZ];
\r
6610 static BOOL sendNextChar = FALSE;
\r
6611 static BOOL quoteNextChar = FALSE;
\r
6612 InputSource *is = consoleInputSource;
\r
6616 switch (message) {
\r
6618 if (!appData.localLineEditing || sendNextChar) {
\r
6619 is->buf[0] = (CHAR) wParam;
\r
6621 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6622 sendNextChar = FALSE;
\r
6625 if (quoteNextChar) {
\r
6626 buf[0] = (char) wParam;
\r
6627 buf[1] = NULLCHAR;
\r
6628 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6629 quoteNextChar = FALSE;
\r
6633 case '\r': /* Enter key */
\r
6634 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6635 if (consoleEcho) SaveInHistory(is->buf);
\r
6636 is->buf[is->count++] = '\n';
\r
6637 is->buf[is->count] = NULLCHAR;
\r
6638 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6639 if (consoleEcho) {
\r
6640 ConsoleOutput(is->buf, is->count, TRUE);
\r
6641 } else if (appData.localLineEditing) {
\r
6642 ConsoleOutput("\n", 1, TRUE);
\r
6645 case '\033': /* Escape key */
\r
6646 SetWindowText(hwnd, "");
\r
6647 cf.cbSize = sizeof(CHARFORMAT);
\r
6648 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6649 if (consoleEcho) {
\r
6650 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6652 cf.crTextColor = COLOR_ECHOOFF;
\r
6654 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6655 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6657 case '\t': /* Tab key */
\r
6658 if (GetKeyState(VK_SHIFT) < 0) {
\r
6660 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6663 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6664 if (buttonDesc[0].hwnd) {
\r
6665 SetFocus(buttonDesc[0].hwnd);
\r
6667 SetFocus(hwndMain);
\r
6671 case '\023': /* Ctrl+S */
\r
6672 sendNextChar = TRUE;
\r
6674 case '\021': /* Ctrl+Q */
\r
6675 quoteNextChar = TRUE;
\r
6685 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6686 p = PrevInHistory(buf);
\r
6688 SetWindowText(hwnd, p);
\r
6689 sel.cpMin = 999999;
\r
6690 sel.cpMax = 999999;
\r
6691 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6696 p = NextInHistory();
\r
6698 SetWindowText(hwnd, p);
\r
6699 sel.cpMin = 999999;
\r
6700 sel.cpMax = 999999;
\r
6701 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6707 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6711 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6715 case WM_MBUTTONDOWN:
\r
6716 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6717 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6719 case WM_RBUTTONUP:
\r
6720 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6721 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6722 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6726 hmenu = LoadMenu(hInst, "InputMenu");
\r
6727 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6728 if (sel.cpMin == sel.cpMax) {
\r
6729 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6730 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6732 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6733 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6735 pt.x = LOWORD(lParam);
\r
6736 pt.y = HIWORD(lParam);
\r
6737 MenuPopup(hwnd, pt, hmenu, -1);
\r
6741 switch (LOWORD(wParam)) {
\r
6743 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6745 case IDM_SelectAll:
\r
6747 sel.cpMax = -1; /*999999?*/
\r
6748 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6751 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6754 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6757 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6762 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6765 #define CO_MAX 100000
\r
6766 #define CO_TRIM 1000
\r
6769 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6771 static SnapData sd;
\r
6772 HWND hText, hInput;
\r
6774 static int sizeX, sizeY;
\r
6775 int newSizeX, newSizeY;
\r
6779 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6780 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6782 switch (message) {
\r
6784 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6786 ENLINK *pLink = (ENLINK*)lParam;
\r
6787 if (pLink->msg == WM_LBUTTONUP)
\r
6791 tr.chrg = pLink->chrg;
\r
6792 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6793 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6794 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6795 free(tr.lpstrText);
\r
6799 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6800 hwndConsole = hDlg;
\r
6802 consoleTextWindowProc = (WNDPROC)
\r
6803 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6804 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6805 consoleInputWindowProc = (WNDPROC)
\r
6806 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6807 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6808 Colorize(ColorNormal, TRUE);
\r
6809 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6810 ChangedConsoleFont();
\r
6811 GetClientRect(hDlg, &rect);
\r
6812 sizeX = rect.right;
\r
6813 sizeY = rect.bottom;
\r
6814 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6815 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6816 WINDOWPLACEMENT wp;
\r
6817 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6818 wp.length = sizeof(WINDOWPLACEMENT);
\r
6820 wp.showCmd = SW_SHOW;
\r
6821 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6822 wp.rcNormalPosition.left = wpConsole.x;
\r
6823 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6824 wp.rcNormalPosition.top = wpConsole.y;
\r
6825 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6826 SetWindowPlacement(hDlg, &wp);
\r
6829 // [HGM] Chessknight's change 2004-07-13
\r
6830 else { /* Determine Defaults */
\r
6831 WINDOWPLACEMENT wp;
\r
6832 wpConsole.x = wpMain.width + 1;
\r
6833 wpConsole.y = wpMain.y;
\r
6834 wpConsole.width = screenWidth - wpMain.width;
\r
6835 wpConsole.height = wpMain.height;
\r
6836 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6837 wp.length = sizeof(WINDOWPLACEMENT);
\r
6839 wp.showCmd = SW_SHOW;
\r
6840 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6841 wp.rcNormalPosition.left = wpConsole.x;
\r
6842 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6843 wp.rcNormalPosition.top = wpConsole.y;
\r
6844 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6845 SetWindowPlacement(hDlg, &wp);
\r
6848 // Allow hText to highlight URLs and send notifications on them
\r
6849 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6850 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6851 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6852 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6866 if (IsIconic(hDlg)) break;
\r
6867 newSizeX = LOWORD(lParam);
\r
6868 newSizeY = HIWORD(lParam);
\r
6869 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6870 RECT rectText, rectInput;
\r
6872 int newTextHeight, newTextWidth;
\r
6873 GetWindowRect(hText, &rectText);
\r
6874 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6875 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6876 if (newTextHeight < 0) {
\r
6877 newSizeY += -newTextHeight;
\r
6878 newTextHeight = 0;
\r
6880 SetWindowPos(hText, NULL, 0, 0,
\r
6881 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6882 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6883 pt.x = rectInput.left;
\r
6884 pt.y = rectInput.top + newSizeY - sizeY;
\r
6885 ScreenToClient(hDlg, &pt);
\r
6886 SetWindowPos(hInput, NULL,
\r
6887 pt.x, pt.y, /* needs client coords */
\r
6888 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6889 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6895 case WM_GETMINMAXINFO:
\r
6896 /* Prevent resizing window too small */
\r
6897 mmi = (MINMAXINFO *) lParam;
\r
6898 mmi->ptMinTrackSize.x = 100;
\r
6899 mmi->ptMinTrackSize.y = 100;
\r
6902 /* [AS] Snapping */
\r
6903 case WM_ENTERSIZEMOVE:
\r
6904 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6907 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6910 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6912 case WM_EXITSIZEMOVE:
\r
6913 UpdateICSWidth(hText);
\r
6914 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6917 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6925 if (hwndConsole) return;
\r
6926 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6927 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6932 ConsoleOutput(char* data, int length, int forceVisible)
\r
6937 char buf[CO_MAX+1];
\r
6940 static int delayLF = 0;
\r
6941 CHARRANGE savesel, sel;
\r
6943 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6951 while (length--) {
\r
6959 } else if (*p == '\007') {
\r
6960 MyPlaySound(&sounds[(int)SoundBell]);
\r
6967 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6968 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6969 /* Save current selection */
\r
6970 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6971 exlen = GetWindowTextLength(hText);
\r
6972 /* Find out whether current end of text is visible */
\r
6973 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6974 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6975 /* Trim existing text if it's too long */
\r
6976 if (exlen + (q - buf) > CO_MAX) {
\r
6977 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6980 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6981 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6983 savesel.cpMin -= trim;
\r
6984 savesel.cpMax -= trim;
\r
6985 if (exlen < 0) exlen = 0;
\r
6986 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6987 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6989 /* Append the new text */
\r
6990 sel.cpMin = exlen;
\r
6991 sel.cpMax = exlen;
\r
6992 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6993 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6994 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6995 if (forceVisible || exlen == 0 ||
\r
6996 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6997 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6998 /* Scroll to make new end of text visible if old end of text
\r
6999 was visible or new text is an echo of user typein */
\r
7000 sel.cpMin = 9999999;
\r
7001 sel.cpMax = 9999999;
\r
7002 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7003 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7004 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7005 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7007 if (savesel.cpMax == exlen || forceVisible) {
\r
7008 /* Move insert point to new end of text if it was at the old
\r
7009 end of text or if the new text is an echo of user typein */
\r
7010 sel.cpMin = 9999999;
\r
7011 sel.cpMax = 9999999;
\r
7012 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7014 /* Restore previous selection */
\r
7015 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7017 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7024 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7028 COLORREF oldFg, oldBg;
\r
7032 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7034 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7035 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7036 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7039 rect.right = x + squareSize;
\r
7041 rect.bottom = y + squareSize;
\r
7044 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7045 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7046 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7047 &rect, str, strlen(str), NULL);
\r
7049 (void) SetTextColor(hdc, oldFg);
\r
7050 (void) SetBkColor(hdc, oldBg);
\r
7051 (void) SelectObject(hdc, oldFont);
\r
7055 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7056 RECT *rect, char *color, char *flagFell)
\r
7060 COLORREF oldFg, oldBg;
\r
7063 if (appData.clockMode) {
\r
7065 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7067 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7074 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7075 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7077 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7078 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7080 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7084 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7085 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7086 rect, str, strlen(str), NULL);
\r
7087 if(logoHeight > 0 && appData.clockMode) {
\r
7089 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7090 r.top = rect->top + logoHeight/2;
\r
7091 r.left = rect->left;
\r
7092 r.right = rect->right;
\r
7093 r.bottom = rect->bottom;
\r
7094 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7095 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7096 &r, str, strlen(str), NULL);
\r
7098 (void) SetTextColor(hdc, oldFg);
\r
7099 (void) SetBkColor(hdc, oldBg);
\r
7100 (void) SelectObject(hdc, oldFont);
\r
7105 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7111 if( count <= 0 ) {
\r
7112 if (appData.debugMode) {
\r
7113 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7116 return ERROR_INVALID_USER_BUFFER;
\r
7119 ResetEvent(ovl->hEvent);
\r
7120 ovl->Offset = ovl->OffsetHigh = 0;
\r
7121 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7125 err = GetLastError();
\r
7126 if (err == ERROR_IO_PENDING) {
\r
7127 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7131 err = GetLastError();
\r
7138 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7143 ResetEvent(ovl->hEvent);
\r
7144 ovl->Offset = ovl->OffsetHigh = 0;
\r
7145 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7149 err = GetLastError();
\r
7150 if (err == ERROR_IO_PENDING) {
\r
7151 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7155 err = GetLastError();
\r
7161 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7162 void CheckForInputBufferFull( InputSource * is )
\r
7164 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7165 /* Look for end of line */
\r
7166 char * p = is->buf;
\r
7168 while( p < is->next && *p != '\n' ) {
\r
7172 if( p >= is->next ) {
\r
7173 if (appData.debugMode) {
\r
7174 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7177 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7178 is->count = (DWORD) -1;
\r
7179 is->next = is->buf;
\r
7185 InputThread(LPVOID arg)
\r
7190 is = (InputSource *) arg;
\r
7191 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7192 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7193 while (is->hThread != NULL) {
\r
7194 is->error = DoReadFile(is->hFile, is->next,
\r
7195 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7196 &is->count, &ovl);
\r
7197 if (is->error == NO_ERROR) {
\r
7198 is->next += is->count;
\r
7200 if (is->error == ERROR_BROKEN_PIPE) {
\r
7201 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7204 is->count = (DWORD) -1;
\r
7205 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7210 CheckForInputBufferFull( is );
\r
7212 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7214 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7216 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7219 CloseHandle(ovl.hEvent);
\r
7220 CloseHandle(is->hFile);
\r
7222 if (appData.debugMode) {
\r
7223 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7230 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7232 NonOvlInputThread(LPVOID arg)
\r
7239 is = (InputSource *) arg;
\r
7240 while (is->hThread != NULL) {
\r
7241 is->error = ReadFile(is->hFile, is->next,
\r
7242 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7243 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7244 if (is->error == NO_ERROR) {
\r
7245 /* Change CRLF to LF */
\r
7246 if (is->next > is->buf) {
\r
7248 i = is->count + 1;
\r
7256 if (prev == '\r' && *p == '\n') {
\r
7268 if (is->error == ERROR_BROKEN_PIPE) {
\r
7269 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7272 is->count = (DWORD) -1;
\r
7276 CheckForInputBufferFull( is );
\r
7278 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7280 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7282 if (is->count < 0) break; /* Quit on error */
\r
7284 CloseHandle(is->hFile);
\r
7289 SocketInputThread(LPVOID arg)
\r
7293 is = (InputSource *) arg;
\r
7294 while (is->hThread != NULL) {
\r
7295 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7296 if ((int)is->count == SOCKET_ERROR) {
\r
7297 is->count = (DWORD) -1;
\r
7298 is->error = WSAGetLastError();
\r
7300 is->error = NO_ERROR;
\r
7301 is->next += is->count;
\r
7302 if (is->count == 0 && is->second == is) {
\r
7303 /* End of file on stderr; quit with no message */
\r
7307 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7309 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7311 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7317 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7321 is = (InputSource *) lParam;
\r
7322 if (is->lineByLine) {
\r
7323 /* Feed in lines one by one */
\r
7324 char *p = is->buf;
\r
7326 while (q < is->next) {
\r
7327 if (*q++ == '\n') {
\r
7328 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7333 /* Move any partial line to the start of the buffer */
\r
7335 while (p < is->next) {
\r
7340 if (is->error != NO_ERROR || is->count == 0) {
\r
7341 /* Notify backend of the error. Note: If there was a partial
\r
7342 line at the end, it is not flushed through. */
\r
7343 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7346 /* Feed in the whole chunk of input at once */
\r
7347 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7348 is->next = is->buf;
\r
7352 /*---------------------------------------------------------------------------*\
\r
7354 * Menu enables. Used when setting various modes.
\r
7356 \*---------------------------------------------------------------------------*/
\r
7364 GreyRevert(Boolean grey)
\r
7365 { // [HGM] vari: for retracting variations in local mode
\r
7366 HMENU hmenu = GetMenu(hwndMain);
\r
7367 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7368 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7372 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7374 while (enab->item > 0) {
\r
7375 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7380 Enables gnuEnables[] = {
\r
7381 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7382 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7383 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7384 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7385 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7386 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7387 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7388 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7389 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7390 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7391 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7392 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7393 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7397 Enables icsEnables[] = {
\r
7398 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7399 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7400 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7401 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7402 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7403 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7404 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7405 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7406 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7407 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7408 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7409 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7410 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7411 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7412 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7413 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7418 Enables zippyEnables[] = {
\r
7419 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7420 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7421 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7422 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7427 Enables ncpEnables[] = {
\r
7428 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7429 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7437 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7439 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7440 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7441 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7442 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7443 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7444 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7445 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7446 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7447 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7451 Enables trainingOnEnables[] = {
\r
7452 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7453 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7454 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7455 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7456 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7457 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7458 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7459 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7463 Enables trainingOffEnables[] = {
\r
7464 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7465 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7466 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7467 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7468 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7469 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7470 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7471 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7475 /* These modify either ncpEnables or gnuEnables */
\r
7476 Enables cmailEnables[] = {
\r
7477 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7478 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7479 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7480 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7481 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7482 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7483 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7487 Enables machineThinkingEnables[] = {
\r
7488 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7489 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7490 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7491 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7492 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7493 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7494 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7495 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7496 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7497 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7498 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7499 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7500 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7501 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7502 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7506 Enables userThinkingEnables[] = {
\r
7507 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7508 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7509 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7510 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7511 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7512 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7513 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7514 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7515 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7516 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7517 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7518 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7519 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7520 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7521 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7525 /*---------------------------------------------------------------------------*\
\r
7527 * Front-end interface functions exported by XBoard.
\r
7528 * Functions appear in same order as prototypes in frontend.h.
\r
7530 \*---------------------------------------------------------------------------*/
\r
7534 static UINT prevChecked = 0;
\r
7535 static int prevPausing = 0;
\r
7538 if (pausing != prevPausing) {
\r
7539 prevPausing = pausing;
\r
7540 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7541 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7542 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7545 switch (gameMode) {
\r
7546 case BeginningOfGame:
\r
7547 if (appData.icsActive)
\r
7548 nowChecked = IDM_IcsClient;
\r
7549 else if (appData.noChessProgram)
\r
7550 nowChecked = IDM_EditGame;
\r
7552 nowChecked = IDM_MachineBlack;
\r
7554 case MachinePlaysBlack:
\r
7555 nowChecked = IDM_MachineBlack;
\r
7557 case MachinePlaysWhite:
\r
7558 nowChecked = IDM_MachineWhite;
\r
7560 case TwoMachinesPlay:
\r
7561 nowChecked = IDM_TwoMachines;
\r
7564 nowChecked = IDM_AnalysisMode;
\r
7567 nowChecked = IDM_AnalyzeFile;
\r
7570 nowChecked = IDM_EditGame;
\r
7572 case PlayFromGameFile:
\r
7573 nowChecked = IDM_LoadGame;
\r
7575 case EditPosition:
\r
7576 nowChecked = IDM_EditPosition;
\r
7579 nowChecked = IDM_Training;
\r
7581 case IcsPlayingWhite:
\r
7582 case IcsPlayingBlack:
\r
7583 case IcsObserving:
\r
7585 nowChecked = IDM_IcsClient;
\r
7592 if (prevChecked != 0)
\r
7593 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7594 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7595 if (nowChecked != 0)
\r
7596 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7597 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7599 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7600 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7601 MF_BYCOMMAND|MF_ENABLED);
\r
7603 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7604 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7607 prevChecked = nowChecked;
\r
7609 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7610 if (appData.icsActive) {
\r
7611 if (appData.icsEngineAnalyze) {
\r
7612 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7613 MF_BYCOMMAND|MF_CHECKED);
\r
7615 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7616 MF_BYCOMMAND|MF_UNCHECKED);
\r
7624 HMENU hmenu = GetMenu(hwndMain);
\r
7625 SetMenuEnables(hmenu, icsEnables);
\r
7626 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7627 MF_BYPOSITION|MF_ENABLED);
\r
7629 if (appData.zippyPlay) {
\r
7630 SetMenuEnables(hmenu, zippyEnables);
\r
7631 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7632 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7633 MF_BYCOMMAND|MF_ENABLED);
\r
7641 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7647 HMENU hmenu = GetMenu(hwndMain);
\r
7648 SetMenuEnables(hmenu, ncpEnables);
\r
7649 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7650 MF_BYPOSITION|MF_GRAYED);
\r
7651 DrawMenuBar(hwndMain);
\r
7657 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7661 SetTrainingModeOn()
\r
7664 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7665 for (i = 0; i < N_BUTTONS; i++) {
\r
7666 if (buttonDesc[i].hwnd != NULL)
\r
7667 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7672 VOID SetTrainingModeOff()
\r
7675 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7676 for (i = 0; i < N_BUTTONS; i++) {
\r
7677 if (buttonDesc[i].hwnd != NULL)
\r
7678 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7684 SetUserThinkingEnables()
\r
7686 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7690 SetMachineThinkingEnables()
\r
7692 HMENU hMenu = GetMenu(hwndMain);
\r
7693 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7695 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7697 if (gameMode == MachinePlaysBlack) {
\r
7698 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7699 } else if (gameMode == MachinePlaysWhite) {
\r
7700 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7701 } else if (gameMode == TwoMachinesPlay) {
\r
7702 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7708 DisplayTitle(char *str)
\r
7710 char title[MSG_SIZ], *host;
\r
7711 if (str[0] != NULLCHAR) {
\r
7712 strcpy(title, str);
\r
7713 } else if (appData.icsActive) {
\r
7714 if (appData.icsCommPort[0] != NULLCHAR)
\r
7717 host = appData.icsHost;
\r
7718 sprintf(title, "%s: %s", szTitle, host);
\r
7719 } else if (appData.noChessProgram) {
\r
7720 strcpy(title, szTitle);
\r
7722 strcpy(title, szTitle);
\r
7723 strcat(title, ": ");
\r
7724 strcat(title, first.tidy);
\r
7726 SetWindowText(hwndMain, title);
\r
7731 DisplayMessage(char *str1, char *str2)
\r
7735 int remain = MESSAGE_TEXT_MAX - 1;
\r
7738 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7739 messageText[0] = NULLCHAR;
\r
7741 len = strlen(str1);
\r
7742 if (len > remain) len = remain;
\r
7743 strncpy(messageText, str1, len);
\r
7744 messageText[len] = NULLCHAR;
\r
7747 if (*str2 && remain >= 2) {
\r
7749 strcat(messageText, " ");
\r
7752 len = strlen(str2);
\r
7753 if (len > remain) len = remain;
\r
7754 strncat(messageText, str2, len);
\r
7756 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7758 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7762 hdc = GetDC(hwndMain);
\r
7763 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7764 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7765 &messageRect, messageText, strlen(messageText), NULL);
\r
7766 (void) SelectObject(hdc, oldFont);
\r
7767 (void) ReleaseDC(hwndMain, hdc);
\r
7771 DisplayError(char *str, int error)
\r
7773 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7779 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7780 NULL, error, LANG_NEUTRAL,
\r
7781 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7783 sprintf(buf, "%s:\n%s", str, buf2);
\r
7785 ErrorMap *em = errmap;
\r
7786 while (em->err != 0 && em->err != error) em++;
\r
7787 if (em->err != 0) {
\r
7788 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7790 sprintf(buf, "%s:\nError code %d", str, error);
\r
7795 ErrorPopUp("Error", buf);
\r
7800 DisplayMoveError(char *str)
\r
7802 fromX = fromY = -1;
\r
7803 ClearHighlights();
\r
7804 DrawPosition(FALSE, NULL);
\r
7805 if (appData.popupMoveErrors) {
\r
7806 ErrorPopUp("Error", str);
\r
7808 DisplayMessage(str, "");
\r
7809 moveErrorMessageUp = TRUE;
\r
7814 DisplayFatalError(char *str, int error, int exitStatus)
\r
7816 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7818 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7821 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7822 NULL, error, LANG_NEUTRAL,
\r
7823 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7825 sprintf(buf, "%s:\n%s", str, buf2);
\r
7827 ErrorMap *em = errmap;
\r
7828 while (em->err != 0 && em->err != error) em++;
\r
7829 if (em->err != 0) {
\r
7830 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7832 sprintf(buf, "%s:\nError code %d", str, error);
\r
7837 if (appData.debugMode) {
\r
7838 fprintf(debugFP, "%s: %s\n", label, str);
\r
7840 if (appData.popupExitMessage) {
\r
7841 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7842 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7844 ExitEvent(exitStatus);
\r
7849 DisplayInformation(char *str)
\r
7851 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7856 DisplayNote(char *str)
\r
7858 ErrorPopUp("Note", str);
\r
7863 char *title, *question, *replyPrefix;
\r
7868 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7870 static QuestionParams *qp;
\r
7871 char reply[MSG_SIZ];
\r
7874 switch (message) {
\r
7875 case WM_INITDIALOG:
\r
7876 qp = (QuestionParams *) lParam;
\r
7877 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7878 SetWindowText(hDlg, qp->title);
\r
7879 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7880 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7884 switch (LOWORD(wParam)) {
\r
7886 strcpy(reply, qp->replyPrefix);
\r
7887 if (*reply) strcat(reply, " ");
\r
7888 len = strlen(reply);
\r
7889 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7890 strcat(reply, "\n");
\r
7891 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7892 EndDialog(hDlg, TRUE);
\r
7893 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7896 EndDialog(hDlg, FALSE);
\r
7907 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7909 QuestionParams qp;
\r
7913 qp.question = question;
\r
7914 qp.replyPrefix = replyPrefix;
\r
7916 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7917 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7918 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7919 FreeProcInstance(lpProc);
\r
7922 /* [AS] Pick FRC position */
\r
7923 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7925 static int * lpIndexFRC;
\r
7931 case WM_INITDIALOG:
\r
7932 lpIndexFRC = (int *) lParam;
\r
7934 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7936 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7937 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7938 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7939 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7944 switch( LOWORD(wParam) ) {
\r
7946 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7947 EndDialog( hDlg, 0 );
\r
7948 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7951 EndDialog( hDlg, 1 );
\r
7953 case IDC_NFG_Edit:
\r
7954 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7955 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7957 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7960 case IDC_NFG_Random:
\r
7961 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7962 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7975 int index = appData.defaultFrcPosition;
\r
7976 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7978 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7980 if( result == 0 ) {
\r
7981 appData.defaultFrcPosition = index;
\r
7987 /* [AS] Game list options. Refactored by HGM */
\r
7989 HWND gameListOptionsDialog;
\r
7991 // low-level front-end: clear text edit / list widget
\r
7995 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7998 // low-level front-end: clear text edit / list widget
\r
8000 GLT_DeSelectList()
\r
8002 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8005 // low-level front-end: append line to text edit / list widget
\r
8007 GLT_AddToList( char *name )
\r
8010 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8014 // low-level front-end: get line from text edit / list widget
\r
8016 GLT_GetFromList( int index, char *name )
\r
8019 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8025 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8027 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8028 int idx2 = idx1 + delta;
\r
8029 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8031 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8034 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8035 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8036 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8037 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8041 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8045 case WM_INITDIALOG:
\r
8046 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8048 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8050 /* Initialize list */
\r
8051 GLT_TagsToList( lpUserGLT );
\r
8053 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8058 switch( LOWORD(wParam) ) {
\r
8061 EndDialog( hDlg, 0 );
\r
8064 EndDialog( hDlg, 1 );
\r
8067 case IDC_GLT_Default:
\r
8068 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8071 case IDC_GLT_Restore:
\r
8072 GLT_TagsToList( appData.gameListTags );
\r
8076 GLT_MoveSelection( hDlg, -1 );
\r
8079 case IDC_GLT_Down:
\r
8080 GLT_MoveSelection( hDlg, +1 );
\r
8090 int GameListOptions()
\r
8093 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8095 strcpy( lpUserGLT, appData.gameListTags );
\r
8097 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8099 if( result == 0 ) {
\r
8100 /* [AS] Memory leak here! */
\r
8101 appData.gameListTags = strdup( lpUserGLT );
\r
8108 DisplayIcsInteractionTitle(char *str)
\r
8110 char consoleTitle[MSG_SIZ];
\r
8112 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8113 SetWindowText(hwndConsole, consoleTitle);
\r
8117 DrawPosition(int fullRedraw, Board board)
\r
8119 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8122 void NotifyFrontendLogin()
\r
8125 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8131 fromX = fromY = -1;
\r
8132 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8133 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8134 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8135 dragInfo.lastpos = dragInfo.pos;
\r
8136 dragInfo.start.x = dragInfo.start.y = -1;
\r
8137 dragInfo.from = dragInfo.start;
\r
8139 DrawPosition(TRUE, NULL);
\r
8145 CommentPopUp(char *title, char *str)
\r
8147 HWND hwnd = GetActiveWindow();
\r
8148 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8150 SetActiveWindow(hwnd);
\r
8154 CommentPopDown(void)
\r
8156 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8157 if (commentDialog) {
\r
8158 ShowWindow(commentDialog, SW_HIDE);
\r
8160 commentUp = FALSE;
\r
8164 EditCommentPopUp(int index, char *title, char *str)
\r
8166 EitherCommentPopUp(index, title, str, TRUE);
\r
8173 MyPlaySound(&sounds[(int)SoundMove]);
\r
8176 VOID PlayIcsWinSound()
\r
8178 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8181 VOID PlayIcsLossSound()
\r
8183 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8186 VOID PlayIcsDrawSound()
\r
8188 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8191 VOID PlayIcsUnfinishedSound()
\r
8193 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8199 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8207 consoleEcho = TRUE;
\r
8208 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8209 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8210 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8219 consoleEcho = FALSE;
\r
8220 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8221 /* This works OK: set text and background both to the same color */
\r
8223 cf.crTextColor = COLOR_ECHOOFF;
\r
8224 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8225 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8228 /* No Raw()...? */
\r
8230 void Colorize(ColorClass cc, int continuation)
\r
8232 currentColorClass = cc;
\r
8233 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8234 consoleCF.crTextColor = textAttribs[cc].color;
\r
8235 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8236 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8242 static char buf[MSG_SIZ];
\r
8243 DWORD bufsiz = MSG_SIZ;
\r
8245 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8246 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8248 if (!GetUserName(buf, &bufsiz)) {
\r
8249 /*DisplayError("Error getting user name", GetLastError());*/
\r
8250 strcpy(buf, "User");
\r
8258 static char buf[MSG_SIZ];
\r
8259 DWORD bufsiz = MSG_SIZ;
\r
8261 if (!GetComputerName(buf, &bufsiz)) {
\r
8262 /*DisplayError("Error getting host name", GetLastError());*/
\r
8263 strcpy(buf, "Unknown");
\r
8270 ClockTimerRunning()
\r
8272 return clockTimerEvent != 0;
\r
8278 if (clockTimerEvent == 0) return FALSE;
\r
8279 KillTimer(hwndMain, clockTimerEvent);
\r
8280 clockTimerEvent = 0;
\r
8285 StartClockTimer(long millisec)
\r
8287 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8288 (UINT) millisec, NULL);
\r
8292 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8295 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8297 if(appData.noGUI) return;
\r
8298 hdc = GetDC(hwndMain);
\r
8299 if (!IsIconic(hwndMain)) {
\r
8300 DisplayAClock(hdc, timeRemaining, highlight,
\r
8301 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8303 if (highlight && iconCurrent == iconBlack) {
\r
8304 iconCurrent = iconWhite;
\r
8305 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8306 if (IsIconic(hwndMain)) {
\r
8307 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8310 (void) ReleaseDC(hwndMain, hdc);
\r
8312 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8316 DisplayBlackClock(long timeRemaining, int highlight)
\r
8319 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8321 if(appData.noGUI) return;
\r
8322 hdc = GetDC(hwndMain);
\r
8323 if (!IsIconic(hwndMain)) {
\r
8324 DisplayAClock(hdc, timeRemaining, highlight,
\r
8325 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8327 if (highlight && iconCurrent == iconWhite) {
\r
8328 iconCurrent = iconBlack;
\r
8329 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8330 if (IsIconic(hwndMain)) {
\r
8331 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8334 (void) ReleaseDC(hwndMain, hdc);
\r
8336 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8341 LoadGameTimerRunning()
\r
8343 return loadGameTimerEvent != 0;
\r
8347 StopLoadGameTimer()
\r
8349 if (loadGameTimerEvent == 0) return FALSE;
\r
8350 KillTimer(hwndMain, loadGameTimerEvent);
\r
8351 loadGameTimerEvent = 0;
\r
8356 StartLoadGameTimer(long millisec)
\r
8358 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8359 (UINT) millisec, NULL);
\r
8367 char fileTitle[MSG_SIZ];
\r
8369 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8370 f = OpenFileDialog(hwndMain, "a", defName,
\r
8371 appData.oldSaveStyle ? "gam" : "pgn",
\r
8373 "Save Game to File", NULL, fileTitle, NULL);
\r
8375 SaveGame(f, 0, "");
\r
8382 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8384 if (delayedTimerEvent != 0) {
\r
8385 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8386 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8388 KillTimer(hwndMain, delayedTimerEvent);
\r
8389 delayedTimerEvent = 0;
\r
8390 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8391 delayedTimerCallback();
\r
8393 delayedTimerCallback = cb;
\r
8394 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8395 (UINT) millisec, NULL);
\r
8398 DelayedEventCallback
\r
8401 if (delayedTimerEvent) {
\r
8402 return delayedTimerCallback;
\r
8409 CancelDelayedEvent()
\r
8411 if (delayedTimerEvent) {
\r
8412 KillTimer(hwndMain, delayedTimerEvent);
\r
8413 delayedTimerEvent = 0;
\r
8417 DWORD GetWin32Priority(int nice)
\r
8418 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8420 REALTIME_PRIORITY_CLASS 0x00000100
\r
8421 HIGH_PRIORITY_CLASS 0x00000080
\r
8422 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8423 NORMAL_PRIORITY_CLASS 0x00000020
\r
8424 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8425 IDLE_PRIORITY_CLASS 0x00000040
\r
8427 if (nice < -15) return 0x00000080;
\r
8428 if (nice < 0) return 0x00008000;
\r
8429 if (nice == 0) return 0x00000020;
\r
8430 if (nice < 15) return 0x00004000;
\r
8431 return 0x00000040;
\r
8434 /* Start a child process running the given program.
\r
8435 The process's standard output can be read from "from", and its
\r
8436 standard input can be written to "to".
\r
8437 Exit with fatal error if anything goes wrong.
\r
8438 Returns an opaque pointer that can be used to destroy the process
\r
8442 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8444 #define BUFSIZE 4096
\r
8446 HANDLE hChildStdinRd, hChildStdinWr,
\r
8447 hChildStdoutRd, hChildStdoutWr;
\r
8448 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8449 SECURITY_ATTRIBUTES saAttr;
\r
8451 PROCESS_INFORMATION piProcInfo;
\r
8452 STARTUPINFO siStartInfo;
\r
8454 char buf[MSG_SIZ];
\r
8457 if (appData.debugMode) {
\r
8458 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8463 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8464 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8465 saAttr.bInheritHandle = TRUE;
\r
8466 saAttr.lpSecurityDescriptor = NULL;
\r
8469 * The steps for redirecting child's STDOUT:
\r
8470 * 1. Create anonymous pipe to be STDOUT for child.
\r
8471 * 2. Create a noninheritable duplicate of read handle,
\r
8472 * and close the inheritable read handle.
\r
8475 /* Create a pipe for the child's STDOUT. */
\r
8476 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8477 return GetLastError();
\r
8480 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8481 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8482 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8483 FALSE, /* not inherited */
\r
8484 DUPLICATE_SAME_ACCESS);
\r
8486 return GetLastError();
\r
8488 CloseHandle(hChildStdoutRd);
\r
8491 * The steps for redirecting child's STDIN:
\r
8492 * 1. Create anonymous pipe to be STDIN for child.
\r
8493 * 2. Create a noninheritable duplicate of write handle,
\r
8494 * and close the inheritable write handle.
\r
8497 /* Create a pipe for the child's STDIN. */
\r
8498 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8499 return GetLastError();
\r
8502 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8503 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8504 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8505 FALSE, /* not inherited */
\r
8506 DUPLICATE_SAME_ACCESS);
\r
8508 return GetLastError();
\r
8510 CloseHandle(hChildStdinWr);
\r
8512 /* Arrange to (1) look in dir for the child .exe file, and
\r
8513 * (2) have dir be the child's working directory. Interpret
\r
8514 * dir relative to the directory WinBoard loaded from. */
\r
8515 GetCurrentDirectory(MSG_SIZ, buf);
\r
8516 SetCurrentDirectory(installDir);
\r
8517 SetCurrentDirectory(dir);
\r
8519 /* Now create the child process. */
\r
8521 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8522 siStartInfo.lpReserved = NULL;
\r
8523 siStartInfo.lpDesktop = NULL;
\r
8524 siStartInfo.lpTitle = NULL;
\r
8525 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8526 siStartInfo.cbReserved2 = 0;
\r
8527 siStartInfo.lpReserved2 = NULL;
\r
8528 siStartInfo.hStdInput = hChildStdinRd;
\r
8529 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8530 siStartInfo.hStdError = hChildStdoutWr;
\r
8532 fSuccess = CreateProcess(NULL,
\r
8533 cmdLine, /* command line */
\r
8534 NULL, /* process security attributes */
\r
8535 NULL, /* primary thread security attrs */
\r
8536 TRUE, /* handles are inherited */
\r
8537 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8538 NULL, /* use parent's environment */
\r
8540 &siStartInfo, /* STARTUPINFO pointer */
\r
8541 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8543 err = GetLastError();
\r
8544 SetCurrentDirectory(buf); /* return to prev directory */
\r
8549 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8550 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8551 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8554 /* Close the handles we don't need in the parent */
\r
8555 CloseHandle(piProcInfo.hThread);
\r
8556 CloseHandle(hChildStdinRd);
\r
8557 CloseHandle(hChildStdoutWr);
\r
8559 /* Prepare return value */
\r
8560 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8561 cp->kind = CPReal;
\r
8562 cp->hProcess = piProcInfo.hProcess;
\r
8563 cp->pid = piProcInfo.dwProcessId;
\r
8564 cp->hFrom = hChildStdoutRdDup;
\r
8565 cp->hTo = hChildStdinWrDup;
\r
8567 *pr = (void *) cp;
\r
8569 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8570 2000 where engines sometimes don't see the initial command(s)
\r
8571 from WinBoard and hang. I don't understand how that can happen,
\r
8572 but the Sleep is harmless, so I've put it in. Others have also
\r
8573 reported what may be the same problem, so hopefully this will fix
\r
8574 it for them too. */
\r
8582 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8584 ChildProc *cp; int result;
\r
8586 cp = (ChildProc *) pr;
\r
8587 if (cp == NULL) return;
\r
8589 switch (cp->kind) {
\r
8591 /* TerminateProcess is considered harmful, so... */
\r
8592 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8593 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8594 /* The following doesn't work because the chess program
\r
8595 doesn't "have the same console" as WinBoard. Maybe
\r
8596 we could arrange for this even though neither WinBoard
\r
8597 nor the chess program uses a console for stdio? */
\r
8598 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8600 /* [AS] Special termination modes for misbehaving programs... */
\r
8601 if( signal == 9 ) {
\r
8602 result = TerminateProcess( cp->hProcess, 0 );
\r
8604 if ( appData.debugMode) {
\r
8605 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8608 else if( signal == 10 ) {
\r
8609 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8611 if( dw != WAIT_OBJECT_0 ) {
\r
8612 result = TerminateProcess( cp->hProcess, 0 );
\r
8614 if ( appData.debugMode) {
\r
8615 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8621 CloseHandle(cp->hProcess);
\r
8625 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8629 closesocket(cp->sock);
\r
8634 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8635 closesocket(cp->sock);
\r
8636 closesocket(cp->sock2);
\r
8644 InterruptChildProcess(ProcRef pr)
\r
8648 cp = (ChildProc *) pr;
\r
8649 if (cp == NULL) return;
\r
8650 switch (cp->kind) {
\r
8652 /* The following doesn't work because the chess program
\r
8653 doesn't "have the same console" as WinBoard. Maybe
\r
8654 we could arrange for this even though neither WinBoard
\r
8655 nor the chess program uses a console for stdio */
\r
8656 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8661 /* Can't interrupt */
\r
8665 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8672 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8674 char cmdLine[MSG_SIZ];
\r
8676 if (port[0] == NULLCHAR) {
\r
8677 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8679 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8681 return StartChildProcess(cmdLine, "", pr);
\r
8685 /* Code to open TCP sockets */
\r
8688 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8693 struct sockaddr_in sa, mysa;
\r
8694 struct hostent FAR *hp;
\r
8695 unsigned short uport;
\r
8696 WORD wVersionRequested;
\r
8699 /* Initialize socket DLL */
\r
8700 wVersionRequested = MAKEWORD(1, 1);
\r
8701 err = WSAStartup(wVersionRequested, &wsaData);
\r
8702 if (err != 0) return err;
\r
8705 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8706 err = WSAGetLastError();
\r
8711 /* Bind local address using (mostly) don't-care values.
\r
8713 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8714 mysa.sin_family = AF_INET;
\r
8715 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8716 uport = (unsigned short) 0;
\r
8717 mysa.sin_port = htons(uport);
\r
8718 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8719 == SOCKET_ERROR) {
\r
8720 err = WSAGetLastError();
\r
8725 /* Resolve remote host name */
\r
8726 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8727 if (!(hp = gethostbyname(host))) {
\r
8728 unsigned int b0, b1, b2, b3;
\r
8730 err = WSAGetLastError();
\r
8732 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8733 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8734 hp->h_addrtype = AF_INET;
\r
8736 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8737 hp->h_addr_list[0] = (char *) malloc(4);
\r
8738 hp->h_addr_list[0][0] = (char) b0;
\r
8739 hp->h_addr_list[0][1] = (char) b1;
\r
8740 hp->h_addr_list[0][2] = (char) b2;
\r
8741 hp->h_addr_list[0][3] = (char) b3;
\r
8747 sa.sin_family = hp->h_addrtype;
\r
8748 uport = (unsigned short) atoi(port);
\r
8749 sa.sin_port = htons(uport);
\r
8750 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8752 /* Make connection */
\r
8753 if (connect(s, (struct sockaddr *) &sa,
\r
8754 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8755 err = WSAGetLastError();
\r
8760 /* Prepare return value */
\r
8761 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8762 cp->kind = CPSock;
\r
8764 *pr = (ProcRef *) cp;
\r
8770 OpenCommPort(char *name, ProcRef *pr)
\r
8775 char fullname[MSG_SIZ];
\r
8777 if (*name != '\\')
\r
8778 sprintf(fullname, "\\\\.\\%s", name);
\r
8780 strcpy(fullname, name);
\r
8782 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8783 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8784 if (h == (HANDLE) -1) {
\r
8785 return GetLastError();
\r
8789 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8791 /* Accumulate characters until a 100ms pause, then parse */
\r
8792 ct.ReadIntervalTimeout = 100;
\r
8793 ct.ReadTotalTimeoutMultiplier = 0;
\r
8794 ct.ReadTotalTimeoutConstant = 0;
\r
8795 ct.WriteTotalTimeoutMultiplier = 0;
\r
8796 ct.WriteTotalTimeoutConstant = 0;
\r
8797 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8799 /* Prepare return value */
\r
8800 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8801 cp->kind = CPComm;
\r
8804 *pr = (ProcRef *) cp;
\r
8810 OpenLoopback(ProcRef *pr)
\r
8812 DisplayFatalError("Not implemented", 0, 1);
\r
8818 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8823 struct sockaddr_in sa, mysa;
\r
8824 struct hostent FAR *hp;
\r
8825 unsigned short uport;
\r
8826 WORD wVersionRequested;
\r
8829 char stderrPortStr[MSG_SIZ];
\r
8831 /* Initialize socket DLL */
\r
8832 wVersionRequested = MAKEWORD(1, 1);
\r
8833 err = WSAStartup(wVersionRequested, &wsaData);
\r
8834 if (err != 0) return err;
\r
8836 /* Resolve remote host name */
\r
8837 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8838 if (!(hp = gethostbyname(host))) {
\r
8839 unsigned int b0, b1, b2, b3;
\r
8841 err = WSAGetLastError();
\r
8843 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8844 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8845 hp->h_addrtype = AF_INET;
\r
8847 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8848 hp->h_addr_list[0] = (char *) malloc(4);
\r
8849 hp->h_addr_list[0][0] = (char) b0;
\r
8850 hp->h_addr_list[0][1] = (char) b1;
\r
8851 hp->h_addr_list[0][2] = (char) b2;
\r
8852 hp->h_addr_list[0][3] = (char) b3;
\r
8858 sa.sin_family = hp->h_addrtype;
\r
8859 uport = (unsigned short) 514;
\r
8860 sa.sin_port = htons(uport);
\r
8861 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8863 /* Bind local socket to unused "privileged" port address
\r
8865 s = INVALID_SOCKET;
\r
8866 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8867 mysa.sin_family = AF_INET;
\r
8868 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8869 for (fromPort = 1023;; fromPort--) {
\r
8870 if (fromPort < 0) {
\r
8872 return WSAEADDRINUSE;
\r
8874 if (s == INVALID_SOCKET) {
\r
8875 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8876 err = WSAGetLastError();
\r
8881 uport = (unsigned short) fromPort;
\r
8882 mysa.sin_port = htons(uport);
\r
8883 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8884 == SOCKET_ERROR) {
\r
8885 err = WSAGetLastError();
\r
8886 if (err == WSAEADDRINUSE) continue;
\r
8890 if (connect(s, (struct sockaddr *) &sa,
\r
8891 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8892 err = WSAGetLastError();
\r
8893 if (err == WSAEADDRINUSE) {
\r
8904 /* Bind stderr local socket to unused "privileged" port address
\r
8906 s2 = INVALID_SOCKET;
\r
8907 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8908 mysa.sin_family = AF_INET;
\r
8909 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8910 for (fromPort = 1023;; fromPort--) {
\r
8911 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8912 if (fromPort < 0) {
\r
8913 (void) closesocket(s);
\r
8915 return WSAEADDRINUSE;
\r
8917 if (s2 == INVALID_SOCKET) {
\r
8918 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8919 err = WSAGetLastError();
\r
8925 uport = (unsigned short) fromPort;
\r
8926 mysa.sin_port = htons(uport);
\r
8927 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8928 == SOCKET_ERROR) {
\r
8929 err = WSAGetLastError();
\r
8930 if (err == WSAEADDRINUSE) continue;
\r
8931 (void) closesocket(s);
\r
8935 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8936 err = WSAGetLastError();
\r
8937 if (err == WSAEADDRINUSE) {
\r
8939 s2 = INVALID_SOCKET;
\r
8942 (void) closesocket(s);
\r
8943 (void) closesocket(s2);
\r
8949 prevStderrPort = fromPort; // remember port used
\r
8950 sprintf(stderrPortStr, "%d", fromPort);
\r
8952 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8953 err = WSAGetLastError();
\r
8954 (void) closesocket(s);
\r
8955 (void) closesocket(s2);
\r
8960 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8961 err = WSAGetLastError();
\r
8962 (void) closesocket(s);
\r
8963 (void) closesocket(s2);
\r
8967 if (*user == NULLCHAR) user = UserName();
\r
8968 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8969 err = WSAGetLastError();
\r
8970 (void) closesocket(s);
\r
8971 (void) closesocket(s2);
\r
8975 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8976 err = WSAGetLastError();
\r
8977 (void) closesocket(s);
\r
8978 (void) closesocket(s2);
\r
8983 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8984 err = WSAGetLastError();
\r
8985 (void) closesocket(s);
\r
8986 (void) closesocket(s2);
\r
8990 (void) closesocket(s2); /* Stop listening */
\r
8992 /* Prepare return value */
\r
8993 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8994 cp->kind = CPRcmd;
\r
8997 *pr = (ProcRef *) cp;
\r
9004 AddInputSource(ProcRef pr, int lineByLine,
\r
9005 InputCallback func, VOIDSTAR closure)
\r
9007 InputSource *is, *is2 = NULL;
\r
9008 ChildProc *cp = (ChildProc *) pr;
\r
9010 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9011 is->lineByLine = lineByLine;
\r
9013 is->closure = closure;
\r
9014 is->second = NULL;
\r
9015 is->next = is->buf;
\r
9016 if (pr == NoProc) {
\r
9017 is->kind = CPReal;
\r
9018 consoleInputSource = is;
\r
9020 is->kind = cp->kind;
\r
9022 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9023 we create all threads suspended so that the is->hThread variable can be
\r
9024 safely assigned, then let the threads start with ResumeThread.
\r
9026 switch (cp->kind) {
\r
9028 is->hFile = cp->hFrom;
\r
9029 cp->hFrom = NULL; /* now owned by InputThread */
\r
9031 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9032 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9036 is->hFile = cp->hFrom;
\r
9037 cp->hFrom = NULL; /* now owned by InputThread */
\r
9039 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9040 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9044 is->sock = cp->sock;
\r
9046 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9047 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9051 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9053 is->sock = cp->sock;
\r
9055 is2->sock = cp->sock2;
\r
9056 is2->second = is2;
\r
9058 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9059 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9061 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9062 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9066 if( is->hThread != NULL ) {
\r
9067 ResumeThread( is->hThread );
\r
9070 if( is2 != NULL && is2->hThread != NULL ) {
\r
9071 ResumeThread( is2->hThread );
\r
9075 return (InputSourceRef) is;
\r
9079 RemoveInputSource(InputSourceRef isr)
\r
9083 is = (InputSource *) isr;
\r
9084 is->hThread = NULL; /* tell thread to stop */
\r
9085 CloseHandle(is->hThread);
\r
9086 if (is->second != NULL) {
\r
9087 is->second->hThread = NULL;
\r
9088 CloseHandle(is->second->hThread);
\r
9092 int no_wrap(char *message, int count)
\r
9094 ConsoleOutput(message, count, FALSE);
\r
9099 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9102 int outCount = SOCKET_ERROR;
\r
9103 ChildProc *cp = (ChildProc *) pr;
\r
9104 static OVERLAPPED ovl;
\r
9105 static int line = 0;
\r
9109 if (appData.noJoin || !appData.useInternalWrap)
\r
9110 return no_wrap(message, count);
\r
9113 int width = get_term_width();
\r
9114 int len = wrap(NULL, message, count, width, &line);
\r
9115 char *msg = malloc(len);
\r
9119 return no_wrap(message, count);
\r
9122 dbgchk = wrap(msg, message, count, width, &line);
\r
9123 if (dbgchk != len && appData.debugMode)
\r
9124 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9125 ConsoleOutput(msg, len, FALSE);
\r
9132 if (ovl.hEvent == NULL) {
\r
9133 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9135 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9137 switch (cp->kind) {
\r
9140 outCount = send(cp->sock, message, count, 0);
\r
9141 if (outCount == SOCKET_ERROR) {
\r
9142 *outError = WSAGetLastError();
\r
9144 *outError = NO_ERROR;
\r
9149 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9150 &dOutCount, NULL)) {
\r
9151 *outError = NO_ERROR;
\r
9152 outCount = (int) dOutCount;
\r
9154 *outError = GetLastError();
\r
9159 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9160 &dOutCount, &ovl);
\r
9161 if (*outError == NO_ERROR) {
\r
9162 outCount = (int) dOutCount;
\r
9170 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9173 /* Ignore delay, not implemented for WinBoard */
\r
9174 return OutputToProcess(pr, message, count, outError);
\r
9179 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9180 char *buf, int count, int error)
\r
9182 DisplayFatalError("Not implemented", 0, 1);
\r
9185 /* see wgamelist.c for Game List functions */
\r
9186 /* see wedittags.c for Edit Tags functions */
\r
9193 char buf[MSG_SIZ];
\r
9196 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9197 f = fopen(buf, "r");
\r
9199 ProcessICSInitScript(f);
\r
9207 StartAnalysisClock()
\r
9209 if (analysisTimerEvent) return;
\r
9210 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9211 (UINT) 2000, NULL);
\r
9215 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9217 highlightInfo.sq[0].x = fromX;
\r
9218 highlightInfo.sq[0].y = fromY;
\r
9219 highlightInfo.sq[1].x = toX;
\r
9220 highlightInfo.sq[1].y = toY;
\r
9226 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9227 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9231 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9233 premoveHighlightInfo.sq[0].x = fromX;
\r
9234 premoveHighlightInfo.sq[0].y = fromY;
\r
9235 premoveHighlightInfo.sq[1].x = toX;
\r
9236 premoveHighlightInfo.sq[1].y = toY;
\r
9240 ClearPremoveHighlights()
\r
9242 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9243 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9247 ShutDownFrontEnd()
\r
9249 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9250 DeleteClipboardTempFiles();
\r
9256 if (IsIconic(hwndMain))
\r
9257 ShowWindow(hwndMain, SW_RESTORE);
\r
9259 SetActiveWindow(hwndMain);
\r
9263 * Prototypes for animation support routines
\r
9265 static void ScreenSquare(int column, int row, POINT * pt);
\r
9266 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9267 POINT frames[], int * nFrames);
\r
9271 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9272 { // [HGM] atomic: animate blast wave
\r
9274 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9275 explodeInfo.fromX = fromX;
\r
9276 explodeInfo.fromY = fromY;
\r
9277 explodeInfo.toX = toX;
\r
9278 explodeInfo.toY = toY;
\r
9279 for(i=1; i<nFrames; i++) {
\r
9280 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9281 DrawPosition(FALSE, NULL);
\r
9282 Sleep(appData.animSpeed);
\r
9284 explodeInfo.radius = 0;
\r
9285 DrawPosition(TRUE, NULL);
\r
9291 AnimateMove(board, fromX, fromY, toX, toY)
\r
9298 ChessSquare piece;
\r
9299 POINT start, finish, mid;
\r
9300 POINT frames[kFactor * 2 + 1];
\r
9303 if (!appData.animate) return;
\r
9304 if (doingSizing) return;
\r
9305 if (fromY < 0 || fromX < 0) return;
\r
9306 piece = board[fromY][fromX];
\r
9307 if (piece >= EmptySquare) return;
\r
9309 ScreenSquare(fromX, fromY, &start);
\r
9310 ScreenSquare(toX, toY, &finish);
\r
9312 /* All pieces except knights move in straight line */
\r
9313 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9314 mid.x = start.x + (finish.x - start.x) / 2;
\r
9315 mid.y = start.y + (finish.y - start.y) / 2;
\r
9317 /* Knight: make diagonal movement then straight */
\r
9318 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9319 mid.x = start.x + (finish.x - start.x) / 2;
\r
9323 mid.y = start.y + (finish.y - start.y) / 2;
\r
9327 /* Don't use as many frames for very short moves */
\r
9328 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9329 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9331 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9333 animInfo.from.x = fromX;
\r
9334 animInfo.from.y = fromY;
\r
9335 animInfo.to.x = toX;
\r
9336 animInfo.to.y = toY;
\r
9337 animInfo.lastpos = start;
\r
9338 animInfo.piece = piece;
\r
9339 for (n = 0; n < nFrames; n++) {
\r
9340 animInfo.pos = frames[n];
\r
9341 DrawPosition(FALSE, NULL);
\r
9342 animInfo.lastpos = animInfo.pos;
\r
9343 Sleep(appData.animSpeed);
\r
9345 animInfo.pos = finish;
\r
9346 DrawPosition(FALSE, NULL);
\r
9347 animInfo.piece = EmptySquare;
\r
9348 if(gameInfo.variant == VariantAtomic &&
\r
9349 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9350 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9353 /* Convert board position to corner of screen rect and color */
\r
9356 ScreenSquare(column, row, pt)
\r
9357 int column; int row; POINT * pt;
\r
9360 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9361 pt->y = lineGap + row * (squareSize + lineGap);
\r
9363 pt->x = lineGap + column * (squareSize + lineGap);
\r
9364 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9368 /* Generate a series of frame coords from start->mid->finish.
\r
9369 The movement rate doubles until the half way point is
\r
9370 reached, then halves back down to the final destination,
\r
9371 which gives a nice slow in/out effect. The algorithmn
\r
9372 may seem to generate too many intermediates for short
\r
9373 moves, but remember that the purpose is to attract the
\r
9374 viewers attention to the piece about to be moved and
\r
9375 then to where it ends up. Too few frames would be less
\r
9379 Tween(start, mid, finish, factor, frames, nFrames)
\r
9380 POINT * start; POINT * mid;
\r
9381 POINT * finish; int factor;
\r
9382 POINT frames[]; int * nFrames;
\r
9384 int n, fraction = 1, count = 0;
\r
9386 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9387 for (n = 0; n < factor; n++)
\r
9389 for (n = 0; n < factor; n++) {
\r
9390 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9391 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9393 fraction = fraction / 2;
\r
9397 frames[count] = *mid;
\r
9400 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9402 for (n = 0; n < factor; n++) {
\r
9403 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9404 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9406 fraction = fraction * 2;
\r
9412 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9414 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9416 EvalGraphSet( first, last, current, pvInfoList );
\r