2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
234 int cliWidth, cliHeight;
\r
237 SizeInfo sizeInfo[] =
\r
239 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
240 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
241 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
242 { "petite", 33, 1, 1, 1, 0, 0 },
\r
243 { "slim", 37, 2, 1, 0, 0, 0 },
\r
244 { "small", 40, 2, 1, 0, 0, 0 },
\r
245 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
246 { "middling", 49, 2, 0, 0, 0, 0 },
\r
247 { "average", 54, 2, 0, 0, 0, 0 },
\r
248 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
249 { "medium", 64, 3, 0, 0, 0, 0 },
\r
250 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
251 { "large", 80, 3, 0, 0, 0, 0 },
\r
252 { "big", 87, 3, 0, 0, 0, 0 },
\r
253 { "huge", 95, 3, 0, 0, 0, 0 },
\r
254 { "giant", 108, 3, 0, 0, 0, 0 },
\r
255 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
256 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
257 { NULL, 0, 0, 0, 0, 0, 0 }
\r
260 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
261 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
263 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
264 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
265 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
266 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
267 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
268 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
269 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
270 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
271 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
272 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
273 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
274 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
275 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
276 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
277 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
278 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
279 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
280 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
283 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
292 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
293 #define N_BUTTONS 5
\r
295 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
297 {"<<", IDM_ToStart, NULL, NULL},
\r
298 {"<", IDM_Backward, NULL, NULL},
\r
299 {"P", IDM_Pause, NULL, NULL},
\r
300 {">", IDM_Forward, NULL, NULL},
\r
301 {">>", IDM_ToEnd, NULL, NULL},
\r
304 int tinyLayout = 0, smallLayout = 0;
\r
305 #define MENU_BAR_ITEMS 7
\r
306 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
307 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
308 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
312 MySound sounds[(int)NSoundClasses];
\r
313 MyTextAttribs textAttribs[(int)NColorClasses];
\r
315 MyColorizeAttribs colorizeAttribs[] = {
\r
316 { (COLORREF)0, 0, "Shout Text" },
\r
317 { (COLORREF)0, 0, "SShout/CShout" },
\r
318 { (COLORREF)0, 0, "Channel 1 Text" },
\r
319 { (COLORREF)0, 0, "Channel Text" },
\r
320 { (COLORREF)0, 0, "Kibitz Text" },
\r
321 { (COLORREF)0, 0, "Tell Text" },
\r
322 { (COLORREF)0, 0, "Challenge Text" },
\r
323 { (COLORREF)0, 0, "Request Text" },
\r
324 { (COLORREF)0, 0, "Seek Text" },
\r
325 { (COLORREF)0, 0, "Normal Text" },
\r
326 { (COLORREF)0, 0, "None" }
\r
331 static char *commentTitle;
\r
332 static char *commentText;
\r
333 static int commentIndex;
\r
334 static Boolean editComment = FALSE;
\r
337 char errorTitle[MSG_SIZ];
\r
338 char errorMessage[2*MSG_SIZ];
\r
339 HWND errorDialog = NULL;
\r
340 BOOLEAN moveErrorMessageUp = FALSE;
\r
341 BOOLEAN consoleEcho = TRUE;
\r
342 CHARFORMAT consoleCF;
\r
343 COLORREF consoleBackgroundColor;
\r
345 char *programVersion;
\r
351 typedef int CPKind;
\r
360 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
363 #define INPUT_SOURCE_BUF_SIZE 4096
\r
365 typedef struct _InputSource {
\r
372 char buf[INPUT_SOURCE_BUF_SIZE];
\r
376 InputCallback func;
\r
377 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
381 InputSource *consoleInputSource;
\r
386 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
387 VOID ConsoleCreate();
\r
389 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
390 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
391 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
392 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
394 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
395 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
396 void ParseIcsTextMenu(char *icsTextMenuString);
\r
397 VOID PopUpMoveDialog(char firstchar);
\r
398 VOID PopUpNameDialog(char firstchar);
\r
399 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
403 int GameListOptions();
\r
405 int dummy; // [HGM] for obsolete args
\r
407 HWND hwndMain = NULL; /* root window*/
\r
408 HWND hwndConsole = NULL;
\r
409 HWND commentDialog = NULL;
\r
410 HWND moveHistoryDialog = NULL;
\r
411 HWND evalGraphDialog = NULL;
\r
412 HWND engineOutputDialog = NULL;
\r
413 HWND gameListDialog = NULL;
\r
414 HWND editTagsDialog = NULL;
\r
416 int commentUp = FALSE;
\r
418 WindowPlacement wpMain;
\r
419 WindowPlacement wpConsole;
\r
420 WindowPlacement wpComment;
\r
421 WindowPlacement wpMoveHistory;
\r
422 WindowPlacement wpEvalGraph;
\r
423 WindowPlacement wpEngineOutput;
\r
424 WindowPlacement wpGameList;
\r
425 WindowPlacement wpTags;
\r
427 VOID EngineOptionsPopup(); // [HGM] settings
\r
429 VOID GothicPopUp(char *title, VariantClass variant);
\r
431 * Setting "frozen" should disable all user input other than deleting
\r
432 * the window. We do this while engines are initializing themselves.
\r
434 static int frozen = 0;
\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
441 if (frozen) return;
\r
443 hmenu = GetMenu(hwndMain);
\r
444 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
445 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
447 DrawMenuBar(hwndMain);
\r
450 /* Undo a FreezeUI */
\r
456 if (!frozen) return;
\r
458 hmenu = GetMenu(hwndMain);
\r
459 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
460 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
462 DrawMenuBar(hwndMain);
\r
465 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
467 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
473 #define JAWS_ALT_INTERCEPT
\r
474 #define JAWS_KB_NAVIGATION
\r
475 #define JAWS_MENU_ITEMS
\r
476 #define JAWS_SILENCE
\r
477 #define JAWS_REPLAY
\r
479 #define JAWS_COPYRIGHT
\r
480 #define JAWS_DELETE(X) X
\r
481 #define SAYMACHINEMOVE()
\r
485 /*---------------------------------------------------------------------------*\
\r
489 \*---------------------------------------------------------------------------*/
\r
492 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
493 LPSTR lpCmdLine, int nCmdShow)
\r
496 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
497 // INITCOMMONCONTROLSEX ex;
\r
501 LoadLibrary("RICHED32.DLL");
\r
502 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
504 if (!InitApplication(hInstance)) {
\r
507 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
513 // InitCommonControlsEx(&ex);
\r
514 InitCommonControls();
\r
516 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
517 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
518 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
520 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
522 while (GetMessage(&msg, /* message structure */
\r
523 NULL, /* handle of window receiving the message */
\r
524 0, /* lowest message to examine */
\r
525 0)) /* highest message to examine */
\r
528 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
529 // [HGM] navigate: switch between all windows with tab
\r
530 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
531 int i, currentElement = 0;
\r
533 // first determine what element of the chain we come from (if any)
\r
534 if(appData.icsActive) {
\r
535 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
536 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
538 if(engineOutputDialog && EngineOutputIsUp()) {
\r
539 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
540 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
542 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
543 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
545 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
546 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
547 if(msg.hwnd == e1) currentElement = 2; else
\r
548 if(msg.hwnd == e2) currentElement = 3; else
\r
549 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
550 if(msg.hwnd == mh) currentElement = 4; else
\r
551 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
552 if(msg.hwnd == hText) currentElement = 5; else
\r
553 if(msg.hwnd == hInput) currentElement = 6; else
\r
554 for (i = 0; i < N_BUTTONS; i++) {
\r
555 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
558 // determine where to go to
\r
559 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
561 currentElement = (currentElement + direction) % 7;
\r
562 switch(currentElement) {
\r
564 h = hwndMain; break; // passing this case always makes the loop exit
\r
566 h = buttonDesc[0].hwnd; break; // could be NULL
\r
568 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
571 if(!EngineOutputIsUp()) continue;
\r
574 if(!MoveHistoryIsUp()) continue;
\r
576 // case 6: // input to eval graph does not seem to get here!
\r
577 // if(!EvalGraphIsUp()) continue;
\r
578 // h = evalGraphDialog; break;
\r
580 if(!appData.icsActive) continue;
\r
584 if(!appData.icsActive) continue;
\r
590 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
591 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
594 continue; // this message now has been processed
\r
598 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
599 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
600 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
601 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
602 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
603 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
604 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
605 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
606 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
607 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
608 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
609 for(i=0; i<MAX_CHAT; i++)
\r
610 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
613 if(done) continue; // [HGM] chat: end patch
\r
614 TranslateMessage(&msg); /* Translates virtual key codes */
\r
615 DispatchMessage(&msg); /* Dispatches message to window */
\r
620 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
623 /*---------------------------------------------------------------------------*\
\r
625 * Initialization functions
\r
627 \*---------------------------------------------------------------------------*/
\r
631 { // update user logo if necessary
\r
632 static char oldUserName[MSG_SIZ], *curName;
\r
634 if(appData.autoLogo) {
\r
635 curName = UserName();
\r
636 if(strcmp(curName, oldUserName)) {
\r
637 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
638 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
639 strcpy(oldUserName, curName);
\r
645 InitApplication(HINSTANCE hInstance)
\r
649 /* Fill in window class structure with parameters that describe the */
\r
652 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
653 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
654 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
655 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
656 wc.hInstance = hInstance; /* Owner of this class */
\r
657 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
658 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
659 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
660 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
661 wc.lpszClassName = szAppName; /* Name to register as */
\r
663 /* Register the window class and return success/failure code. */
\r
664 if (!RegisterClass(&wc)) return FALSE;
\r
666 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
667 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
669 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
670 wc.hInstance = hInstance;
\r
671 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
672 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
673 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
674 wc.lpszMenuName = NULL;
\r
675 wc.lpszClassName = szConsoleName;
\r
677 if (!RegisterClass(&wc)) return FALSE;
\r
682 /* Set by InitInstance, used by EnsureOnScreen */
\r
683 int screenHeight, screenWidth;
\r
686 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
688 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
689 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
690 if (*x > screenWidth - 32) *x = 0;
\r
691 if (*y > screenHeight - 32) *y = 0;
\r
692 if (*x < minX) *x = minX;
\r
693 if (*y < minY) *y = minY;
\r
697 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
699 HWND hwnd; /* Main window handle. */
\r
701 WINDOWPLACEMENT wp;
\r
704 hInst = hInstance; /* Store instance handle in our global variable */
\r
705 programName = szAppName;
\r
707 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
708 *filepart = NULLCHAR;
\r
710 GetCurrentDirectory(MSG_SIZ, installDir);
\r
712 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
713 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
714 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
715 /* xboard, and older WinBoards, controlled the move sound with the
\r
716 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
717 always turn the option on (so that the backend will call us),
\r
718 then let the user turn the sound off by setting it to silence if
\r
719 desired. To accommodate old winboard.ini files saved by old
\r
720 versions of WinBoard, we also turn off the sound if the option
\r
721 was initially set to false. [HGM] taken out of InitAppData */
\r
722 if (!appData.ringBellAfterMoves) {
\r
723 sounds[(int)SoundMove].name = strdup("");
\r
724 appData.ringBellAfterMoves = TRUE;
\r
726 if (appData.debugMode) {
\r
727 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
728 setbuf(debugFP, NULL);
\r
733 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
734 // InitEngineUCI( installDir, &second );
\r
736 /* Create a main window for this application instance. */
\r
737 hwnd = CreateWindow(szAppName, szTitle,
\r
738 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
739 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
740 NULL, NULL, hInstance, NULL);
\r
743 /* If window could not be created, return "failure" */
\r
748 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
749 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
750 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
752 if (first.programLogo == NULL && appData.debugMode) {
\r
753 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
755 } else if(appData.autoLogo) {
\r
756 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
758 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
759 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
763 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
764 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
766 if (second.programLogo == NULL && appData.debugMode) {
\r
767 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
769 } else if(appData.autoLogo) {
\r
771 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
772 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
773 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
775 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
776 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
777 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
783 iconWhite = LoadIcon(hInstance, "icon_white");
\r
784 iconBlack = LoadIcon(hInstance, "icon_black");
\r
785 iconCurrent = iconWhite;
\r
786 InitDrawingColors();
\r
787 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
788 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
789 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
790 /* Compute window size for each board size, and use the largest
\r
791 size that fits on this screen as the default. */
\r
792 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
793 if (boardSize == (BoardSize)-1 &&
\r
794 winH <= screenHeight
\r
795 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
796 && winW <= screenWidth) {
\r
797 boardSize = (BoardSize)ibs;
\r
801 InitDrawingSizes(boardSize, 0);
\r
803 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
805 /* [AS] Load textures if specified */
\r
806 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
808 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
809 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
810 liteBackTextureMode = appData.liteBackTextureMode;
\r
812 if (liteBackTexture == NULL && appData.debugMode) {
\r
813 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
817 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
818 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
819 darkBackTextureMode = appData.darkBackTextureMode;
\r
821 if (darkBackTexture == NULL && appData.debugMode) {
\r
822 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
826 mysrandom( (unsigned) time(NULL) );
\r
828 /* [AS] Restore layout */
\r
829 if( wpMoveHistory.visible ) {
\r
830 MoveHistoryPopUp();
\r
833 if( wpEvalGraph.visible ) {
\r
837 if( wpEngineOutput.visible ) {
\r
838 EngineOutputPopUp();
\r
841 /* Make the window visible; update its client area; and return "success" */
\r
842 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
843 wp.length = sizeof(WINDOWPLACEMENT);
\r
845 wp.showCmd = nCmdShow;
\r
846 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
847 wp.rcNormalPosition.left = wpMain.x;
\r
848 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
849 wp.rcNormalPosition.top = wpMain.y;
\r
850 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
851 SetWindowPlacement(hwndMain, &wp);
\r
853 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
855 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
856 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
860 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
861 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
863 ShowWindow(hwndConsole, nCmdShow);
\r
864 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
865 char buf[MSG_SIZ], *p = buf, *q;
\r
866 strcpy(buf, appData.chatBoxes);
\r
868 q = strchr(p, ';');
\r
870 if(*p) ChatPopUp(p);
\r
873 SetActiveWindow(hwndConsole);
\r
875 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
876 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
885 HMENU hmenu = GetMenu(hwndMain);
\r
887 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
888 MF_BYCOMMAND|((appData.icsActive &&
\r
889 *appData.icsCommPort != NULLCHAR) ?
\r
890 MF_ENABLED : MF_GRAYED));
\r
891 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
892 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
893 MF_CHECKED : MF_UNCHECKED));
\r
896 //---------------------------------------------------------------------------------------------------------
\r
898 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
899 #define XBOARD FALSE
\r
901 #define OPTCHAR "/"
\r
902 #define SEPCHAR "="
\r
906 // front-end part of option handling
\r
909 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
911 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
912 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
915 lf->lfEscapement = 0;
\r
916 lf->lfOrientation = 0;
\r
917 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
918 lf->lfItalic = mfp->italic;
\r
919 lf->lfUnderline = mfp->underline;
\r
920 lf->lfStrikeOut = mfp->strikeout;
\r
921 lf->lfCharSet = mfp->charset;
\r
922 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
923 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
924 lf->lfQuality = DEFAULT_QUALITY;
\r
925 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
926 strcpy(lf->lfFaceName, mfp->faceName);
\r
930 CreateFontInMF(MyFont *mf)
\r
932 LFfromMFP(&mf->lf, &mf->mfp);
\r
933 if (mf->hf) DeleteObject(mf->hf);
\r
934 mf->hf = CreateFontIndirect(&mf->lf);
\r
937 // [HGM] This platform-dependent table provides the location for storing the color info
\r
939 colorVariable[] = {
\r
944 &highlightSquareColor,
\r
945 &premoveHighlightColor,
\r
947 &consoleBackgroundColor,
\r
948 &appData.fontForeColorWhite,
\r
949 &appData.fontBackColorWhite,
\r
950 &appData.fontForeColorBlack,
\r
951 &appData.fontBackColorBlack,
\r
952 &appData.evalHistColorWhite,
\r
953 &appData.evalHistColorBlack,
\r
954 &appData.highlightArrowColor,
\r
957 /* Command line font name parser. NULL name means do nothing.
\r
958 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
959 For backward compatibility, syntax without the colon is also
\r
960 accepted, but font names with digits in them won't work in that case.
\r
963 ParseFontName(char *name, MyFontParams *mfp)
\r
966 if (name == NULL) return;
\r
968 q = strchr(p, ':');
\r
970 if (q - p >= sizeof(mfp->faceName))
\r
971 ExitArgError("Font name too long:", name);
\r
972 memcpy(mfp->faceName, p, q - p);
\r
973 mfp->faceName[q - p] = NULLCHAR;
\r
977 while (*p && !isdigit(*p)) {
\r
979 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
980 ExitArgError("Font name too long:", name);
\r
982 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
985 if (!*p) ExitArgError("Font point size missing:", name);
\r
986 mfp->pointSize = (float) atof(p);
\r
987 mfp->bold = (strchr(p, 'b') != NULL);
\r
988 mfp->italic = (strchr(p, 'i') != NULL);
\r
989 mfp->underline = (strchr(p, 'u') != NULL);
\r
990 mfp->strikeout = (strchr(p, 's') != NULL);
\r
991 mfp->charset = DEFAULT_CHARSET;
\r
992 q = strchr(p, 'c');
\r
994 mfp->charset = (BYTE) atoi(q+1);
\r
998 ParseFont(char *name, int number)
\r
999 { // wrapper to shield back-end from 'font'
\r
1000 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1005 { // in WB we have a 2D array of fonts; this initializes their description
\r
1007 /* Point font array elements to structures and
\r
1008 parse default font names */
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 font[j][i] = &fontRec[j][i];
\r
1012 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1019 { // here we create the actual fonts from the selected descriptions
\r
1021 for (i=0; i<NUM_FONTS; i++) {
\r
1022 for (j=0; j<NUM_SIZES; j++) {
\r
1023 CreateFontInMF(font[j][i]);
\r
1027 /* Color name parser.
\r
1028 X version accepts X color names, but this one
\r
1029 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1031 ParseColorName(char *name)
\r
1033 int red, green, blue, count;
\r
1034 char buf[MSG_SIZ];
\r
1036 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1038 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1039 &red, &green, &blue);
\r
1042 sprintf(buf, "Can't parse color name %s", name);
\r
1043 DisplayError(buf, 0);
\r
1044 return RGB(0, 0, 0);
\r
1046 return PALETTERGB(red, green, blue);
\r
1050 ParseColor(int n, char *name)
\r
1051 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1052 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1056 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1058 char *e = argValue;
\r
1062 if (*e == 'b') eff |= CFE_BOLD;
\r
1063 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1064 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1065 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1066 else if (*e == '#' || isdigit(*e)) break;
\r
1070 *color = ParseColorName(e);
\r
1074 ParseTextAttribs(ColorClass cc, char *s)
\r
1075 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1076 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1077 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1081 ParseBoardSize(void *addr, char *name)
\r
1082 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1083 BoardSize bs = SizeTiny;
\r
1084 while (sizeInfo[bs].name != NULL) {
\r
1085 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1086 *(BoardSize *)addr = bs;
\r
1091 ExitArgError("Unrecognized board size value", name);
\r
1096 { // [HGM] import name from appData first
\r
1099 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1100 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1101 textAttribs[cc].sound.data = NULL;
\r
1102 MyLoadSound(&textAttribs[cc].sound);
\r
1104 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1105 textAttribs[cc].sound.name = strdup("");
\r
1106 textAttribs[cc].sound.data = NULL;
\r
1108 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1109 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1110 sounds[sc].data = NULL;
\r
1111 MyLoadSound(&sounds[sc]);
\r
1116 SetCommPortDefaults()
\r
1118 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1119 dcb.DCBlength = sizeof(DCB);
\r
1120 dcb.BaudRate = 9600;
\r
1121 dcb.fBinary = TRUE;
\r
1122 dcb.fParity = FALSE;
\r
1123 dcb.fOutxCtsFlow = FALSE;
\r
1124 dcb.fOutxDsrFlow = FALSE;
\r
1125 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1126 dcb.fDsrSensitivity = FALSE;
\r
1127 dcb.fTXContinueOnXoff = TRUE;
\r
1128 dcb.fOutX = FALSE;
\r
1130 dcb.fNull = FALSE;
\r
1131 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1132 dcb.fAbortOnError = FALSE;
\r
1134 dcb.Parity = SPACEPARITY;
\r
1135 dcb.StopBits = ONESTOPBIT;
\r
1138 // [HGM] args: these three cases taken out to stay in front-end
\r
1140 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1141 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1142 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1143 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1145 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1146 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1147 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1148 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1149 ad->argName, mfp->faceName, mfp->pointSize,
\r
1150 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1151 mfp->bold ? "b" : "",
\r
1152 mfp->italic ? "i" : "",
\r
1153 mfp->underline ? "u" : "",
\r
1154 mfp->strikeout ? "s" : "",
\r
1155 (int)mfp->charset);
\r
1161 { // [HGM] copy the names from the internal WB variables to appData
\r
1164 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1165 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1166 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1167 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1171 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1172 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1173 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1174 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1175 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1176 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1177 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1178 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1179 (ta->effects) ? " " : "",
\r
1180 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1184 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1185 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1186 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1187 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1188 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1192 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1193 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1194 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1198 ParseCommPortSettings(char *s)
\r
1199 { // wrapper to keep dcb from back-end
\r
1200 ParseCommSettings(s, &dcb);
\r
1205 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1206 GetActualPlacement(hwndMain, &wpMain);
\r
1207 GetActualPlacement(hwndConsole, &wpConsole);
\r
1208 GetActualPlacement(commentDialog, &wpComment);
\r
1209 GetActualPlacement(editTagsDialog, &wpTags);
\r
1210 GetActualPlacement(gameListDialog, &wpGameList);
\r
1211 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1212 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1213 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1217 PrintCommPortSettings(FILE *f, char *name)
\r
1218 { // wrapper to shield back-end from DCB
\r
1219 PrintCommSettings(f, name, &dcb);
\r
1223 MySearchPath(char *installDir, char *name, char *fullname)
\r
1225 char *dummy, buf[MSG_SIZ];
\r
1226 if(name[0] == '%' && strchr(name+1, '%')) { // [HGM] recognize %*% as environment variable
\r
1227 strcpy(buf, name+1);
\r
1228 *strchr(buf, '%') = 0;
\r
1229 installDir = getenv(buf);
\r
1230 sprintf(fullname, "%s\\%s", installDir, strchr(name+1, '%')+1);
\r
1231 return strlen(fullname);
\r
1233 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1237 MyGetFullPathName(char *name, char *fullname)
\r
1240 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1245 { // [HGM] args: allows testing if main window is realized from back-end
\r
1246 return hwndMain != NULL;
\r
1250 PopUpStartupDialog()
\r
1254 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1255 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1256 FreeProcInstance(lpProc);
\r
1259 /*---------------------------------------------------------------------------*\
\r
1261 * GDI board drawing routines
\r
1263 \*---------------------------------------------------------------------------*/
\r
1265 /* [AS] Draw square using background texture */
\r
1266 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1271 return; /* Should never happen! */
\r
1274 SetGraphicsMode( dst, GM_ADVANCED );
\r
1281 /* X reflection */
\r
1286 x.eDx = (FLOAT) dw + dx - 1;
\r
1289 SetWorldTransform( dst, &x );
\r
1292 /* Y reflection */
\r
1298 x.eDy = (FLOAT) dh + dy - 1;
\r
1300 SetWorldTransform( dst, &x );
\r
1308 x.eDx = (FLOAT) dx;
\r
1309 x.eDy = (FLOAT) dy;
\r
1312 SetWorldTransform( dst, &x );
\r
1316 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1324 SetWorldTransform( dst, &x );
\r
1326 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1329 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1331 PM_WP = (int) WhitePawn,
\r
1332 PM_WN = (int) WhiteKnight,
\r
1333 PM_WB = (int) WhiteBishop,
\r
1334 PM_WR = (int) WhiteRook,
\r
1335 PM_WQ = (int) WhiteQueen,
\r
1336 PM_WF = (int) WhiteFerz,
\r
1337 PM_WW = (int) WhiteWazir,
\r
1338 PM_WE = (int) WhiteAlfil,
\r
1339 PM_WM = (int) WhiteMan,
\r
1340 PM_WO = (int) WhiteCannon,
\r
1341 PM_WU = (int) WhiteUnicorn,
\r
1342 PM_WH = (int) WhiteNightrider,
\r
1343 PM_WA = (int) WhiteAngel,
\r
1344 PM_WC = (int) WhiteMarshall,
\r
1345 PM_WAB = (int) WhiteCardinal,
\r
1346 PM_WD = (int) WhiteDragon,
\r
1347 PM_WL = (int) WhiteLance,
\r
1348 PM_WS = (int) WhiteCobra,
\r
1349 PM_WV = (int) WhiteFalcon,
\r
1350 PM_WSG = (int) WhiteSilver,
\r
1351 PM_WG = (int) WhiteGrasshopper,
\r
1352 PM_WK = (int) WhiteKing,
\r
1353 PM_BP = (int) BlackPawn,
\r
1354 PM_BN = (int) BlackKnight,
\r
1355 PM_BB = (int) BlackBishop,
\r
1356 PM_BR = (int) BlackRook,
\r
1357 PM_BQ = (int) BlackQueen,
\r
1358 PM_BF = (int) BlackFerz,
\r
1359 PM_BW = (int) BlackWazir,
\r
1360 PM_BE = (int) BlackAlfil,
\r
1361 PM_BM = (int) BlackMan,
\r
1362 PM_BO = (int) BlackCannon,
\r
1363 PM_BU = (int) BlackUnicorn,
\r
1364 PM_BH = (int) BlackNightrider,
\r
1365 PM_BA = (int) BlackAngel,
\r
1366 PM_BC = (int) BlackMarshall,
\r
1367 PM_BG = (int) BlackGrasshopper,
\r
1368 PM_BAB = (int) BlackCardinal,
\r
1369 PM_BD = (int) BlackDragon,
\r
1370 PM_BL = (int) BlackLance,
\r
1371 PM_BS = (int) BlackCobra,
\r
1372 PM_BV = (int) BlackFalcon,
\r
1373 PM_BSG = (int) BlackSilver,
\r
1374 PM_BK = (int) BlackKing
\r
1377 static HFONT hPieceFont = NULL;
\r
1378 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1379 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1380 static int fontBitmapSquareSize = 0;
\r
1381 static char pieceToFontChar[(int) EmptySquare] =
\r
1382 { 'p', 'n', 'b', 'r', 'q',
\r
1383 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1384 'k', 'o', 'm', 'v', 't', 'w',
\r
1385 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1388 extern BOOL SetCharTable( char *table, const char * map );
\r
1389 /* [HGM] moved to backend.c */
\r
1391 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1394 BYTE r1 = GetRValue( color );
\r
1395 BYTE g1 = GetGValue( color );
\r
1396 BYTE b1 = GetBValue( color );
\r
1402 /* Create a uniform background first */
\r
1403 hbrush = CreateSolidBrush( color );
\r
1404 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1405 FillRect( hdc, &rc, hbrush );
\r
1406 DeleteObject( hbrush );
\r
1409 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1410 int steps = squareSize / 2;
\r
1413 for( i=0; i<steps; i++ ) {
\r
1414 BYTE r = r1 - (r1-r2) * i / steps;
\r
1415 BYTE g = g1 - (g1-g2) * i / steps;
\r
1416 BYTE b = b1 - (b1-b2) * i / steps;
\r
1418 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1419 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1420 FillRect( hdc, &rc, hbrush );
\r
1421 DeleteObject(hbrush);
\r
1424 else if( mode == 2 ) {
\r
1425 /* Diagonal gradient, good more or less for every piece */
\r
1426 POINT triangle[3];
\r
1427 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1428 HBRUSH hbrush_old;
\r
1429 int steps = squareSize;
\r
1432 triangle[0].x = squareSize - steps;
\r
1433 triangle[0].y = squareSize;
\r
1434 triangle[1].x = squareSize;
\r
1435 triangle[1].y = squareSize;
\r
1436 triangle[2].x = squareSize;
\r
1437 triangle[2].y = squareSize - steps;
\r
1439 for( i=0; i<steps; i++ ) {
\r
1440 BYTE r = r1 - (r1-r2) * i / steps;
\r
1441 BYTE g = g1 - (g1-g2) * i / steps;
\r
1442 BYTE b = b1 - (b1-b2) * i / steps;
\r
1444 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1445 hbrush_old = SelectObject( hdc, hbrush );
\r
1446 Polygon( hdc, triangle, 3 );
\r
1447 SelectObject( hdc, hbrush_old );
\r
1448 DeleteObject(hbrush);
\r
1453 SelectObject( hdc, hpen );
\r
1458 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1459 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1460 piece: follow the steps as explained below.
\r
1462 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1466 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1470 int backColor = whitePieceColor;
\r
1471 int foreColor = blackPieceColor;
\r
1473 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1474 backColor = appData.fontBackColorWhite;
\r
1475 foreColor = appData.fontForeColorWhite;
\r
1477 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1478 backColor = appData.fontBackColorBlack;
\r
1479 foreColor = appData.fontForeColorBlack;
\r
1483 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1485 hbm_old = SelectObject( hdc, hbm );
\r
1489 rc.right = squareSize;
\r
1490 rc.bottom = squareSize;
\r
1492 /* Step 1: background is now black */
\r
1493 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1495 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1497 pt.x = (squareSize - sz.cx) / 2;
\r
1498 pt.y = (squareSize - sz.cy) / 2;
\r
1500 SetBkMode( hdc, TRANSPARENT );
\r
1501 SetTextColor( hdc, chroma );
\r
1502 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1503 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1505 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1506 /* Step 3: the area outside the piece is filled with white */
\r
1507 // FloodFill( hdc, 0, 0, chroma );
\r
1508 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1509 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1510 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1511 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1512 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1514 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1515 but if the start point is not inside the piece we're lost!
\r
1516 There should be a better way to do this... if we could create a region or path
\r
1517 from the fill operation we would be fine for example.
\r
1519 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1520 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1522 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1523 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1524 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1526 SelectObject( dc2, bm2 );
\r
1527 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1528 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1529 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1530 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1531 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1534 DeleteObject( bm2 );
\r
1537 SetTextColor( hdc, 0 );
\r
1539 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1540 draw the piece again in black for safety.
\r
1542 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1544 SelectObject( hdc, hbm_old );
\r
1546 if( hPieceMask[index] != NULL ) {
\r
1547 DeleteObject( hPieceMask[index] );
\r
1550 hPieceMask[index] = hbm;
\r
1553 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1555 SelectObject( hdc, hbm );
\r
1558 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1559 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1560 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1562 SelectObject( dc1, hPieceMask[index] );
\r
1563 SelectObject( dc2, bm2 );
\r
1564 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1565 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1568 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1569 the piece background and deletes (makes transparent) the rest.
\r
1570 Thanks to that mask, we are free to paint the background with the greates
\r
1571 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1572 We use this, to make gradients and give the pieces a "roundish" look.
\r
1574 SetPieceBackground( hdc, backColor, 2 );
\r
1575 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1579 DeleteObject( bm2 );
\r
1582 SetTextColor( hdc, foreColor );
\r
1583 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1585 SelectObject( hdc, hbm_old );
\r
1587 if( hPieceFace[index] != NULL ) {
\r
1588 DeleteObject( hPieceFace[index] );
\r
1591 hPieceFace[index] = hbm;
\r
1594 static int TranslatePieceToFontPiece( int piece )
\r
1624 case BlackMarshall:
\r
1628 case BlackNightrider:
\r
1634 case BlackUnicorn:
\r
1638 case BlackGrasshopper:
\r
1650 case BlackCardinal:
\r
1657 case WhiteMarshall:
\r
1661 case WhiteNightrider:
\r
1667 case WhiteUnicorn:
\r
1671 case WhiteGrasshopper:
\r
1683 case WhiteCardinal:
\r
1692 void CreatePiecesFromFont()
\r
1695 HDC hdc_window = NULL;
\r
1701 if( fontBitmapSquareSize < 0 ) {
\r
1702 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1706 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1707 fontBitmapSquareSize = -1;
\r
1711 if( fontBitmapSquareSize != squareSize ) {
\r
1712 hdc_window = GetDC( hwndMain );
\r
1713 hdc = CreateCompatibleDC( hdc_window );
\r
1715 if( hPieceFont != NULL ) {
\r
1716 DeleteObject( hPieceFont );
\r
1719 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1720 hPieceMask[i] = NULL;
\r
1721 hPieceFace[i] = NULL;
\r
1727 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1728 fontHeight = appData.fontPieceSize;
\r
1731 fontHeight = (fontHeight * squareSize) / 100;
\r
1733 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1735 lf.lfEscapement = 0;
\r
1736 lf.lfOrientation = 0;
\r
1737 lf.lfWeight = FW_NORMAL;
\r
1739 lf.lfUnderline = 0;
\r
1740 lf.lfStrikeOut = 0;
\r
1741 lf.lfCharSet = DEFAULT_CHARSET;
\r
1742 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1743 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1744 lf.lfQuality = PROOF_QUALITY;
\r
1745 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1746 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1747 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1749 hPieceFont = CreateFontIndirect( &lf );
\r
1751 if( hPieceFont == NULL ) {
\r
1752 fontBitmapSquareSize = -2;
\r
1755 /* Setup font-to-piece character table */
\r
1756 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1757 /* No (or wrong) global settings, try to detect the font */
\r
1758 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1760 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1762 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1763 /* DiagramTT* family */
\r
1764 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1766 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1767 /* Fairy symbols */
\r
1768 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1770 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1771 /* Good Companion (Some characters get warped as literal :-( */
\r
1772 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1773 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1774 SetCharTable(pieceToFontChar, s);
\r
1777 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1778 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1782 /* Create bitmaps */
\r
1783 hfont_old = SelectObject( hdc, hPieceFont );
\r
1784 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1785 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1786 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1788 SelectObject( hdc, hfont_old );
\r
1790 fontBitmapSquareSize = squareSize;
\r
1794 if( hdc != NULL ) {
\r
1798 if( hdc_window != NULL ) {
\r
1799 ReleaseDC( hwndMain, hdc_window );
\r
1804 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1808 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1809 if (gameInfo.event &&
\r
1810 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1811 strcmp(name, "k80s") == 0) {
\r
1812 strcpy(name, "tim");
\r
1814 return LoadBitmap(hinst, name);
\r
1818 /* Insert a color into the program's logical palette
\r
1819 structure. This code assumes the given color is
\r
1820 the result of the RGB or PALETTERGB macro, and it
\r
1821 knows how those macros work (which is documented).
\r
1824 InsertInPalette(COLORREF color)
\r
1826 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1828 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1829 DisplayFatalError("Too many colors", 0, 1);
\r
1830 pLogPal->palNumEntries--;
\r
1834 pe->peFlags = (char) 0;
\r
1835 pe->peRed = (char) (0xFF & color);
\r
1836 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1837 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1843 InitDrawingColors()
\r
1845 if (pLogPal == NULL) {
\r
1846 /* Allocate enough memory for a logical palette with
\r
1847 * PALETTESIZE entries and set the size and version fields
\r
1848 * of the logical palette structure.
\r
1850 pLogPal = (NPLOGPALETTE)
\r
1851 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1852 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1853 pLogPal->palVersion = 0x300;
\r
1855 pLogPal->palNumEntries = 0;
\r
1857 InsertInPalette(lightSquareColor);
\r
1858 InsertInPalette(darkSquareColor);
\r
1859 InsertInPalette(whitePieceColor);
\r
1860 InsertInPalette(blackPieceColor);
\r
1861 InsertInPalette(highlightSquareColor);
\r
1862 InsertInPalette(premoveHighlightColor);
\r
1864 /* create a logical color palette according the information
\r
1865 * in the LOGPALETTE structure.
\r
1867 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1869 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1870 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1871 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1872 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1873 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1874 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1875 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1876 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1877 /* [AS] Force rendering of the font-based pieces */
\r
1878 if( fontBitmapSquareSize > 0 ) {
\r
1879 fontBitmapSquareSize = 0;
\r
1885 BoardWidth(int boardSize, int n)
\r
1886 { /* [HGM] argument n added to allow different width and height */
\r
1887 int lineGap = sizeInfo[boardSize].lineGap;
\r
1889 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1890 lineGap = appData.overrideLineGap;
\r
1893 return (n + 1) * lineGap +
\r
1894 n * sizeInfo[boardSize].squareSize;
\r
1897 /* Respond to board resize by dragging edge */
\r
1899 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1901 BoardSize newSize = NUM_SIZES - 1;
\r
1902 static int recurse = 0;
\r
1903 if (IsIconic(hwndMain)) return;
\r
1904 if (recurse > 0) return;
\r
1906 while (newSize > 0) {
\r
1907 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1908 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1909 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1912 boardSize = newSize;
\r
1913 InitDrawingSizes(boardSize, flags);
\r
1918 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1921 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1923 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1924 ChessSquare piece;
\r
1925 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1927 SIZE clockSize, messageSize;
\r
1929 char buf[MSG_SIZ];
\r
1931 HMENU hmenu = GetMenu(hwndMain);
\r
1932 RECT crect, wrect, oldRect;
\r
1934 LOGBRUSH logbrush;
\r
1936 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1937 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1939 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1940 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1942 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1943 oldRect.top = wpMain.y;
\r
1944 oldRect.right = wpMain.x + wpMain.width;
\r
1945 oldRect.bottom = wpMain.y + wpMain.height;
\r
1947 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1948 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1949 squareSize = sizeInfo[boardSize].squareSize;
\r
1950 lineGap = sizeInfo[boardSize].lineGap;
\r
1951 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1953 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1954 lineGap = appData.overrideLineGap;
\r
1957 if (tinyLayout != oldTinyLayout) {
\r
1958 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1960 style &= ~WS_SYSMENU;
\r
1961 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1962 "&Minimize\tCtrl+F4");
\r
1964 style |= WS_SYSMENU;
\r
1965 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1967 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1969 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1970 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1971 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1973 DrawMenuBar(hwndMain);
\r
1976 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1977 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1979 /* Get text area sizes */
\r
1980 hdc = GetDC(hwndMain);
\r
1981 if (appData.clockMode) {
\r
1982 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1984 sprintf(buf, "White");
\r
1986 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1987 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1988 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1989 str = "We only care about the height here";
\r
1990 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1991 SelectObject(hdc, oldFont);
\r
1992 ReleaseDC(hwndMain, hdc);
\r
1994 /* Compute where everything goes */
\r
1995 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1996 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1997 logoHeight = 2*clockSize.cy;
\r
1998 leftLogoRect.left = OUTER_MARGIN;
\r
1999 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2000 leftLogoRect.top = OUTER_MARGIN;
\r
2001 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2003 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2004 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2005 rightLogoRect.top = OUTER_MARGIN;
\r
2006 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2009 whiteRect.left = leftLogoRect.right;
\r
2010 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2011 whiteRect.top = OUTER_MARGIN;
\r
2012 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2014 blackRect.right = rightLogoRect.left;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.top = whiteRect.top;
\r
2017 blackRect.bottom = whiteRect.bottom;
\r
2019 whiteRect.left = OUTER_MARGIN;
\r
2020 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2021 whiteRect.top = OUTER_MARGIN;
\r
2022 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2024 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2025 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2026 blackRect.top = whiteRect.top;
\r
2027 blackRect.bottom = whiteRect.bottom;
\r
2029 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2032 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2033 if (appData.showButtonBar) {
\r
2034 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2035 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2037 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2039 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2040 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2042 boardRect.left = OUTER_MARGIN;
\r
2043 boardRect.right = boardRect.left + boardWidth;
\r
2044 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2045 boardRect.bottom = boardRect.top + boardHeight;
\r
2047 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2048 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2049 oldBoardSize = boardSize;
\r
2050 oldTinyLayout = tinyLayout;
\r
2051 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2052 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2053 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2054 winW *= 1 + twoBoards;
\r
2055 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2056 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2057 wpMain.height = winH; // without disturbing window attachments
\r
2058 GetWindowRect(hwndMain, &wrect);
\r
2059 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2060 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2062 // [HGM] placement: let attached windows follow size change.
\r
2063 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2064 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2065 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2066 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2067 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2069 /* compensate if menu bar wrapped */
\r
2070 GetClientRect(hwndMain, &crect);
\r
2071 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2072 wpMain.height += offby;
\r
2074 case WMSZ_TOPLEFT:
\r
2075 SetWindowPos(hwndMain, NULL,
\r
2076 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2077 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2080 case WMSZ_TOPRIGHT:
\r
2082 SetWindowPos(hwndMain, NULL,
\r
2083 wrect.left, wrect.bottom - wpMain.height,
\r
2084 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2087 case WMSZ_BOTTOMLEFT:
\r
2089 SetWindowPos(hwndMain, NULL,
\r
2090 wrect.right - wpMain.width, wrect.top,
\r
2091 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2094 case WMSZ_BOTTOMRIGHT:
\r
2098 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2099 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2104 for (i = 0; i < N_BUTTONS; i++) {
\r
2105 if (buttonDesc[i].hwnd != NULL) {
\r
2106 DestroyWindow(buttonDesc[i].hwnd);
\r
2107 buttonDesc[i].hwnd = NULL;
\r
2109 if (appData.showButtonBar) {
\r
2110 buttonDesc[i].hwnd =
\r
2111 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2112 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2113 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2114 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2115 (HMENU) buttonDesc[i].id,
\r
2116 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2118 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2119 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2120 MAKELPARAM(FALSE, 0));
\r
2122 if (buttonDesc[i].id == IDM_Pause)
\r
2123 hwndPause = buttonDesc[i].hwnd;
\r
2124 buttonDesc[i].wndproc = (WNDPROC)
\r
2125 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2128 if (gridPen != NULL) DeleteObject(gridPen);
\r
2129 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2130 if (premovePen != NULL) DeleteObject(premovePen);
\r
2131 if (lineGap != 0) {
\r
2132 logbrush.lbStyle = BS_SOLID;
\r
2133 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2135 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2136 lineGap, &logbrush, 0, NULL);
\r
2137 logbrush.lbColor = highlightSquareColor;
\r
2139 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2140 lineGap, &logbrush, 0, NULL);
\r
2142 logbrush.lbColor = premoveHighlightColor;
\r
2144 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2145 lineGap, &logbrush, 0, NULL);
\r
2147 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2148 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2149 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2150 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2151 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2152 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2153 BOARD_WIDTH * (squareSize + lineGap);
\r
2154 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2156 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2157 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2158 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2159 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2160 lineGap / 2 + (i * (squareSize + lineGap));
\r
2161 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2162 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2163 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2167 /* [HGM] Licensing requirement */
\r
2169 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2172 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2174 GothicPopUp( "", VariantNormal);
\r
2177 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2179 /* Load piece bitmaps for this board size */
\r
2180 for (i=0; i<=2; i++) {
\r
2181 for (piece = WhitePawn;
\r
2182 (int) piece < (int) BlackPawn;
\r
2183 piece = (ChessSquare) ((int) piece + 1)) {
\r
2184 if (pieceBitmap[i][piece] != NULL)
\r
2185 DeleteObject(pieceBitmap[i][piece]);
\r
2189 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2190 // Orthodox Chess pieces
\r
2191 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2192 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2193 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2194 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2195 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2196 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2197 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2198 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2199 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2200 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2201 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2202 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2203 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2204 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2205 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2206 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2207 // in Shogi, Hijack the unused Queen for Lance
\r
2208 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2209 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2210 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2212 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2217 if(squareSize <= 72 && squareSize >= 33) {
\r
2218 /* A & C are available in most sizes now */
\r
2219 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2220 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2221 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2222 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2223 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2224 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2225 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2226 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2227 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2228 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2229 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2232 } else { // Smirf-like
\r
2233 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2237 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2238 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2241 } else { // WinBoard standard
\r
2242 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2249 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2250 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2259 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2271 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2272 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2273 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2281 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2282 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2285 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2286 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2287 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2288 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2289 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2290 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2291 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2292 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2293 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2295 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2296 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2297 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2298 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2299 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2300 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2301 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2302 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2303 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2304 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2305 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2306 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2309 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2310 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2311 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2312 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2313 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2314 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2315 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2316 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2317 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2318 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2319 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2320 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2321 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2322 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2323 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2327 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2328 /* special Shogi support in this size */
\r
2329 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2330 for (piece = WhitePawn;
\r
2331 (int) piece < (int) BlackPawn;
\r
2332 piece = (ChessSquare) ((int) piece + 1)) {
\r
2333 if (pieceBitmap[i][piece] != NULL)
\r
2334 DeleteObject(pieceBitmap[i][piece]);
\r
2337 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2342 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2344 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2345 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2346 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2347 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2348 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2349 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2350 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2351 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2356 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2358 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2359 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2360 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2361 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2362 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2363 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2364 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2365 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2370 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2372 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2373 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2374 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2375 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2376 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2377 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2378 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2384 PieceBitmap(ChessSquare p, int kind)
\r
2386 if ((int) p >= (int) BlackPawn)
\r
2387 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2389 return pieceBitmap[kind][(int) p];
\r
2392 /***************************************************************/
\r
2394 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2395 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2397 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2398 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2402 SquareToPos(int row, int column, int * x, int * y)
\r
2405 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2406 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2408 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2409 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2414 DrawCoordsOnDC(HDC hdc)
\r
2416 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2417 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2418 char str[2] = { NULLCHAR, NULLCHAR };
\r
2419 int oldMode, oldAlign, x, y, start, i;
\r
2423 if (!appData.showCoords)
\r
2426 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2428 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2429 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2430 oldAlign = GetTextAlign(hdc);
\r
2431 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2433 y = boardRect.top + lineGap;
\r
2434 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2436 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2437 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2438 str[0] = files[start + i];
\r
2439 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2440 y += squareSize + lineGap;
\r
2443 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2445 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2446 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2447 str[0] = ranks[start + i];
\r
2448 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2449 x += squareSize + lineGap;
\r
2452 SelectObject(hdc, oldBrush);
\r
2453 SetBkMode(hdc, oldMode);
\r
2454 SetTextAlign(hdc, oldAlign);
\r
2455 SelectObject(hdc, oldFont);
\r
2459 DrawGridOnDC(HDC hdc)
\r
2463 if (lineGap != 0) {
\r
2464 oldPen = SelectObject(hdc, gridPen);
\r
2465 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2466 SelectObject(hdc, oldPen);
\r
2470 #define HIGHLIGHT_PEN 0
\r
2471 #define PREMOVE_PEN 1
\r
2474 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2477 HPEN oldPen, hPen;
\r
2478 if (lineGap == 0) return;
\r
2480 x1 = boardRect.left +
\r
2481 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2482 y1 = boardRect.top +
\r
2483 lineGap/2 + y * (squareSize + lineGap);
\r
2485 x1 = boardRect.left +
\r
2486 lineGap/2 + x * (squareSize + lineGap);
\r
2487 y1 = boardRect.top +
\r
2488 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2490 hPen = pen ? premovePen : highlightPen;
\r
2491 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2492 MoveToEx(hdc, x1, y1, NULL);
\r
2493 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2494 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2495 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2496 LineTo(hdc, x1, y1);
\r
2497 SelectObject(hdc, oldPen);
\r
2501 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2504 for (i=0; i<2; i++) {
\r
2505 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2506 DrawHighlightOnDC(hdc, TRUE,
\r
2507 h->sq[i].x, h->sq[i].y,
\r
2512 /* Note: sqcolor is used only in monoMode */
\r
2513 /* Note that this code is largely duplicated in woptions.c,
\r
2514 function DrawSampleSquare, so that needs to be updated too */
\r
2516 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2518 HBITMAP oldBitmap;
\r
2522 if (appData.blindfold) return;
\r
2524 /* [AS] Use font-based pieces if needed */
\r
2525 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\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
3292 } else { // nr == 1
\r
3293 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3294 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3295 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3296 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3297 for (i=0; i<2; i++) {
\r
3298 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3299 partnerHighlightInfo.sq[i].y >= 0) {
\r
3300 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3301 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3302 clips[num_clips++] =
\r
3303 CreateRectRgn(x - lineGap, y - lineGap,
\r
3304 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3306 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3307 oldPartnerHighlight.sq[i].y >= 0) {
\r
3308 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3309 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3310 clips[num_clips++] =
\r
3311 CreateRectRgn(x - lineGap, y - lineGap,
\r
3312 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3317 fullrepaint = TRUE;
\r
3320 /* Create a buffer bitmap - this is the actual bitmap
\r
3321 * being written to. When all the work is done, we can
\r
3322 * copy it to the real DC (the screen). This avoids
\r
3323 * the problems with flickering.
\r
3325 GetClientRect(hwndMain, &Rect);
\r
3326 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3327 Rect.bottom-Rect.top+1);
\r
3328 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3329 if (!appData.monoMode) {
\r
3330 SelectPalette(hdcmem, hPal, FALSE);
\r
3333 /* Create clips for dragging */
\r
3334 if (!fullrepaint) {
\r
3335 if (dragInfo.from.x >= 0) {
\r
3336 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3337 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3339 if (dragInfo.start.x >= 0) {
\r
3340 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3341 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3343 if (dragInfo.pos.x >= 0) {
\r
3344 x = dragInfo.pos.x - squareSize / 2;
\r
3345 y = dragInfo.pos.y - squareSize / 2;
\r
3346 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3348 if (dragInfo.lastpos.x >= 0) {
\r
3349 x = dragInfo.lastpos.x - squareSize / 2;
\r
3350 y = dragInfo.lastpos.y - squareSize / 2;
\r
3351 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3355 /* Are we animating a move?
\r
3357 * - remove the piece from the board (temporarely)
\r
3358 * - calculate the clipping region
\r
3360 if (!fullrepaint) {
\r
3361 if (animInfo.piece != EmptySquare) {
\r
3362 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3363 x = boardRect.left + animInfo.lastpos.x;
\r
3364 y = boardRect.top + animInfo.lastpos.y;
\r
3365 x2 = boardRect.left + animInfo.pos.x;
\r
3366 y2 = boardRect.top + animInfo.pos.y;
\r
3367 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3368 /* Slight kludge. The real problem is that after AnimateMove is
\r
3369 done, the position on the screen does not match lastDrawn.
\r
3370 This currently causes trouble only on e.p. captures in
\r
3371 atomic, where the piece moves to an empty square and then
\r
3372 explodes. The old and new positions both had an empty square
\r
3373 at the destination, but animation has drawn a piece there and
\r
3374 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3375 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3379 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3380 if (num_clips == 0)
\r
3381 fullrepaint = TRUE;
\r
3383 /* Set clipping on the memory DC */
\r
3384 if (!fullrepaint) {
\r
3385 SelectClipRgn(hdcmem, clips[0]);
\r
3386 for (x = 1; x < num_clips; x++) {
\r
3387 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3388 abort(); // this should never ever happen!
\r
3392 /* Do all the drawing to the memory DC */
\r
3393 if(explodeInfo.radius) { // [HGM] atomic
\r
3395 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3396 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3397 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3398 x += squareSize/2;
\r
3399 y += squareSize/2;
\r
3400 if(!fullrepaint) {
\r
3401 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3402 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3404 DrawGridOnDC(hdcmem);
\r
3405 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3406 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3407 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3408 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3409 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3410 SelectObject(hdcmem, oldBrush);
\r
3412 DrawGridOnDC(hdcmem);
\r
3413 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3414 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3415 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3417 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3418 oldPartnerHighlight = partnerHighlightInfo;
\r
3420 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3422 if(nr == 0) // [HGM] dual: markers only on left board
\r
3423 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3424 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3425 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3426 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3427 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3428 SquareToPos(row, column, &x, &y);
\r
3429 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3430 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3431 SelectObject(hdcmem, oldBrush);
\r
3436 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3437 if(appData.autoLogo) {
\r
3439 switch(gameMode) { // pick logos based on game mode
\r
3440 case IcsObserving:
\r
3441 whiteLogo = second.programLogo; // ICS logo
\r
3442 blackLogo = second.programLogo;
\r
3445 case IcsPlayingWhite:
\r
3446 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3447 blackLogo = second.programLogo; // ICS logo
\r
3449 case IcsPlayingBlack:
\r
3450 whiteLogo = second.programLogo; // ICS logo
\r
3451 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3453 case TwoMachinesPlay:
\r
3454 if(first.twoMachinesColor[0] == 'b') {
\r
3455 whiteLogo = second.programLogo;
\r
3456 blackLogo = first.programLogo;
\r
3459 case MachinePlaysWhite:
\r
3460 blackLogo = userLogo;
\r
3462 case MachinePlaysBlack:
\r
3463 whiteLogo = userLogo;
\r
3464 blackLogo = first.programLogo;
\r
3467 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3468 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3471 if( appData.highlightMoveWithArrow ) {
\r
3472 DrawArrowHighlight(hdcmem);
\r
3475 DrawCoordsOnDC(hdcmem);
\r
3477 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3478 /* to make sure lastDrawn contains what is actually drawn */
\r
3480 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3481 if (dragged_piece != EmptySquare) {
\r
3482 /* [HGM] or restack */
\r
3483 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3484 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3486 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3487 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3488 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3489 x = dragInfo.pos.x - squareSize / 2;
\r
3490 y = dragInfo.pos.y - squareSize / 2;
\r
3491 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3492 ((int) dragged_piece < (int) BlackPawn),
\r
3493 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3496 /* Put the animated piece back into place and draw it */
\r
3497 if (animInfo.piece != EmptySquare) {
\r
3498 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3499 x = boardRect.left + animInfo.pos.x;
\r
3500 y = boardRect.top + animInfo.pos.y;
\r
3501 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3502 ((int) animInfo.piece < (int) BlackPawn),
\r
3503 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3506 /* Release the bufferBitmap by selecting in the old bitmap
\r
3507 * and delete the memory DC
\r
3509 SelectObject(hdcmem, oldBitmap);
\r
3512 /* Set clipping on the target DC */
\r
3513 if (!fullrepaint) {
\r
3514 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3516 GetRgnBox(clips[x], &rect);
\r
3517 DeleteObject(clips[x]);
\r
3518 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3519 rect.right + wpMain.width/2, rect.bottom);
\r
3521 SelectClipRgn(hdc, clips[0]);
\r
3522 for (x = 1; x < num_clips; x++) {
\r
3523 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3524 abort(); // this should never ever happen!
\r
3528 /* Copy the new bitmap onto the screen in one go.
\r
3529 * This way we avoid any flickering
\r
3531 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3532 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3533 boardRect.right - boardRect.left,
\r
3534 boardRect.bottom - boardRect.top,
\r
3535 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3536 if(saveDiagFlag) {
\r
3537 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3538 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3540 GetObject(bufferBitmap, sizeof(b), &b);
\r
3541 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3542 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3543 bih.biWidth = b.bmWidth;
\r
3544 bih.biHeight = b.bmHeight;
\r
3546 bih.biBitCount = b.bmBitsPixel;
\r
3547 bih.biCompression = 0;
\r
3548 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3549 bih.biXPelsPerMeter = 0;
\r
3550 bih.biYPelsPerMeter = 0;
\r
3551 bih.biClrUsed = 0;
\r
3552 bih.biClrImportant = 0;
\r
3553 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3554 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3555 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3556 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3558 wb = b.bmWidthBytes;
\r
3560 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3561 int k = ((int*) pData)[i];
\r
3562 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3563 if(j >= 16) break;
\r
3565 if(j >= nrColors) nrColors = j+1;
\r
3567 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3569 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3570 for(w=0; w<(wb>>2); w+=2) {
\r
3571 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3572 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3573 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3574 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3575 pData[p++] = m | j<<4;
\r
3577 while(p&3) pData[p++] = 0;
\r
3580 wb = ((wb+31)>>5)<<2;
\r
3582 // write BITMAPFILEHEADER
\r
3583 fprintf(diagFile, "BM");
\r
3584 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3585 fputDW(diagFile, 0);
\r
3586 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3587 // write BITMAPINFOHEADER
\r
3588 fputDW(diagFile, 40);
\r
3589 fputDW(diagFile, b.bmWidth);
\r
3590 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3591 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3592 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3593 fputDW(diagFile, 0);
\r
3594 fputDW(diagFile, 0);
\r
3595 fputDW(diagFile, 0);
\r
3596 fputDW(diagFile, 0);
\r
3597 fputDW(diagFile, 0);
\r
3598 fputDW(diagFile, 0);
\r
3599 // write color table
\r
3601 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3602 // write bitmap data
\r
3603 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3604 fputc(pData[i], diagFile);
\r
3608 SelectObject(tmphdc, oldBitmap);
\r
3610 /* Massive cleanup */
\r
3611 for (x = 0; x < num_clips; x++)
\r
3612 DeleteObject(clips[x]);
\r
3615 DeleteObject(bufferBitmap);
\r
3618 ReleaseDC(hwndMain, hdc);
\r
3620 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3622 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3624 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3627 /* CopyBoard(lastDrawn, board);*/
\r
3628 lastDrawnHighlight = highlightInfo;
\r
3629 lastDrawnPremove = premoveHighlightInfo;
\r
3630 lastDrawnFlipView = flipView;
\r
3631 lastDrawnValid[nr] = 1;
\r
3634 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3639 saveDiagFlag = 1; diagFile = f;
\r
3640 HDCDrawPosition(NULL, TRUE, NULL);
\r
3644 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3651 /*---------------------------------------------------------------------------*\
\r
3652 | CLIENT PAINT PROCEDURE
\r
3653 | This is the main event-handler for the WM_PAINT message.
\r
3655 \*---------------------------------------------------------------------------*/
\r
3657 PaintProc(HWND hwnd)
\r
3663 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3664 if (IsIconic(hwnd)) {
\r
3665 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3667 if (!appData.monoMode) {
\r
3668 SelectPalette(hdc, hPal, FALSE);
\r
3669 RealizePalette(hdc);
\r
3671 HDCDrawPosition(hdc, 1, NULL);
\r
3672 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3673 flipView = !flipView; partnerUp = !partnerUp;
\r
3674 HDCDrawPosition(hdc, 1, NULL);
\r
3675 flipView = !flipView; partnerUp = !partnerUp;
\r
3678 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3679 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3680 ETO_CLIPPED|ETO_OPAQUE,
\r
3681 &messageRect, messageText, strlen(messageText), NULL);
\r
3682 SelectObject(hdc, oldFont);
\r
3683 DisplayBothClocks();
\r
3685 EndPaint(hwnd,&ps);
\r
3693 * If the user selects on a border boundary, return -1; if off the board,
\r
3694 * return -2. Otherwise map the event coordinate to the square.
\r
3695 * The offset boardRect.left or boardRect.top must already have been
\r
3696 * subtracted from x.
\r
3698 int EventToSquare(x, limit)
\r
3706 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3708 x /= (squareSize + lineGap);
\r
3720 DropEnable dropEnables[] = {
\r
3721 { 'P', DP_Pawn, "Pawn" },
\r
3722 { 'N', DP_Knight, "Knight" },
\r
3723 { 'B', DP_Bishop, "Bishop" },
\r
3724 { 'R', DP_Rook, "Rook" },
\r
3725 { 'Q', DP_Queen, "Queen" },
\r
3729 SetupDropMenu(HMENU hmenu)
\r
3731 int i, count, enable;
\r
3733 extern char white_holding[], black_holding[];
\r
3734 char item[MSG_SIZ];
\r
3736 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3737 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3738 dropEnables[i].piece);
\r
3740 while (p && *p++ == dropEnables[i].piece) count++;
\r
3741 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3742 enable = count > 0 || !appData.testLegality
\r
3743 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3744 && !appData.icsActive);
\r
3745 ModifyMenu(hmenu, dropEnables[i].command,
\r
3746 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3747 dropEnables[i].command, item);
\r
3751 void DragPieceBegin(int x, int y)
\r
3753 dragInfo.lastpos.x = boardRect.left + x;
\r
3754 dragInfo.lastpos.y = boardRect.top + y;
\r
3755 dragInfo.from.x = fromX;
\r
3756 dragInfo.from.y = fromY;
\r
3757 dragInfo.start = dragInfo.from;
\r
3758 SetCapture(hwndMain);
\r
3761 void DragPieceEnd(int x, int y)
\r
3764 dragInfo.start.x = dragInfo.start.y = -1;
\r
3765 dragInfo.from = dragInfo.start;
\r
3766 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3769 /* Event handler for mouse messages */
\r
3771 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3775 static int recursive = 0;
\r
3777 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3780 if (message == WM_MBUTTONUP) {
\r
3781 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3782 to the middle button: we simulate pressing the left button too!
\r
3784 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3785 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3791 pt.x = LOWORD(lParam);
\r
3792 pt.y = HIWORD(lParam);
\r
3793 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3794 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3795 if (!flipView && y >= 0) {
\r
3796 y = BOARD_HEIGHT - 1 - y;
\r
3798 if (flipView && x >= 0) {
\r
3799 x = BOARD_WIDTH - 1 - x;
\r
3802 switch (message) {
\r
3803 case WM_LBUTTONDOWN:
\r
3804 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3805 if (gameMode == EditPosition) {
\r
3806 SetWhiteToPlayEvent();
\r
3807 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3808 AdjustClock(flipClock, -1);
\r
3809 } else if (gameMode == IcsPlayingBlack ||
\r
3810 gameMode == MachinePlaysWhite) {
\r
3813 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3814 if (gameMode == EditPosition) {
\r
3815 SetBlackToPlayEvent();
\r
3816 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
3817 AdjustClock(!flipClock, -1);
\r
3818 } else if (gameMode == IcsPlayingWhite ||
\r
3819 gameMode == MachinePlaysBlack) {
\r
3823 dragInfo.start.x = dragInfo.start.y = -1;
\r
3824 dragInfo.from = dragInfo.start;
\r
3825 if(fromX == -1 && frozen) { // not sure where this is for
\r
3826 fromX = fromY = -1;
\r
3827 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3830 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3831 DrawPosition(TRUE, NULL);
\r
3834 case WM_LBUTTONUP:
\r
3835 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3836 DrawPosition(TRUE, NULL);
\r
3839 case WM_MOUSEMOVE:
\r
3840 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3841 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3842 if ((appData.animateDragging || appData.highlightDragging)
\r
3843 && (wParam & MK_LBUTTON)
\r
3844 && dragInfo.from.x >= 0)
\r
3846 BOOL full_repaint = FALSE;
\r
3848 if (appData.animateDragging) {
\r
3849 dragInfo.pos = pt;
\r
3851 if (appData.highlightDragging) {
\r
3852 SetHighlights(fromX, fromY, x, y);
\r
3853 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3854 full_repaint = TRUE;
\r
3858 DrawPosition( full_repaint, NULL);
\r
3860 dragInfo.lastpos = dragInfo.pos;
\r
3864 case WM_MOUSEWHEEL: // [DM]
\r
3865 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3866 /* Mouse Wheel is being rolled forward
\r
3867 * Play moves forward
\r
3869 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3870 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3871 /* Mouse Wheel is being rolled backward
\r
3872 * Play moves backward
\r
3874 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3875 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3879 case WM_MBUTTONUP:
\r
3880 case WM_RBUTTONUP:
\r
3882 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3885 case WM_MBUTTONDOWN:
\r
3886 case WM_RBUTTONDOWN:
\r
3889 fromX = fromY = -1;
\r
3890 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3891 dragInfo.start.x = dragInfo.start.y = -1;
\r
3892 dragInfo.from = dragInfo.start;
\r
3893 dragInfo.lastpos = dragInfo.pos;
\r
3894 if (appData.highlightDragging) {
\r
3895 ClearHighlights();
\r
3898 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3899 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3900 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
3901 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3902 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
3906 DrawPosition(TRUE, NULL);
\r
3908 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3911 if (message == WM_MBUTTONDOWN) {
\r
3912 buttonCount = 3; /* even if system didn't think so */
\r
3913 if (wParam & MK_SHIFT)
\r
3914 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3916 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3917 } else { /* message == WM_RBUTTONDOWN */
\r
3918 /* Just have one menu, on the right button. Windows users don't
\r
3919 think to try the middle one, and sometimes other software steals
\r
3920 it, or it doesn't really exist. */
\r
3921 if(gameInfo.variant != VariantShogi)
\r
3922 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3924 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3928 SetCapture(hwndMain);
3931 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3932 SetupDropMenu(hmenu);
\r
3933 MenuPopup(hwnd, pt, hmenu, -1);
\r
3943 /* Preprocess messages for buttons in main window */
\r
3945 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3947 int id = GetWindowLong(hwnd, GWL_ID);
\r
3950 for (i=0; i<N_BUTTONS; i++) {
\r
3951 if (buttonDesc[i].id == id) break;
\r
3953 if (i == N_BUTTONS) return 0;
\r
3954 switch (message) {
\r
3959 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3960 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3967 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3970 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3971 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3972 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3973 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3975 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3977 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3978 PopUpMoveDialog((char)wParam);
\r
3984 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3987 /* Process messages for Promotion dialog box */
\r
3989 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3993 switch (message) {
\r
3994 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3995 /* Center the dialog over the application window */
\r
3996 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3997 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3998 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3999 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4000 SW_SHOW : SW_HIDE);
\r
4001 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4002 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4003 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4004 PieceToChar(WhiteAngel) != '~') ||
\r
4005 (PieceToChar(BlackAngel) >= 'A' &&
\r
4006 PieceToChar(BlackAngel) != '~') ) ?
\r
4007 SW_SHOW : SW_HIDE);
\r
4008 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4009 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4010 PieceToChar(WhiteMarshall) != '~') ||
\r
4011 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4012 PieceToChar(BlackMarshall) != '~') ) ?
\r
4013 SW_SHOW : SW_HIDE);
\r
4014 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4015 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4016 gameInfo.variant != VariantShogi ?
\r
4017 SW_SHOW : SW_HIDE);
\r
4018 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4019 gameInfo.variant != VariantShogi ?
\r
4020 SW_SHOW : SW_HIDE);
\r
4021 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4022 gameInfo.variant == VariantShogi ?
\r
4023 SW_SHOW : SW_HIDE);
\r
4024 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4025 gameInfo.variant == VariantShogi ?
\r
4026 SW_SHOW : SW_HIDE);
\r
4027 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4028 gameInfo.variant == VariantSuper ?
\r
4029 SW_SHOW : SW_HIDE);
\r
4032 case WM_COMMAND: /* message: received a command */
\r
4033 switch (LOWORD(wParam)) {
\r
4035 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4036 ClearHighlights();
\r
4037 DrawPosition(FALSE, NULL);
\r
4040 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4043 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4046 promoChar = PieceToChar(BlackRook);
\r
4049 promoChar = PieceToChar(BlackBishop);
\r
4051 case PB_Chancellor:
\r
4052 promoChar = PieceToChar(BlackMarshall);
\r
4054 case PB_Archbishop:
\r
4055 promoChar = PieceToChar(BlackAngel);
\r
4058 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4063 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4064 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4065 only show the popup when we are already sure the move is valid or
\r
4066 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4067 will figure out it is a promotion from the promoChar. */
\r
4068 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4069 fromX = fromY = -1;
\r
4070 if (!appData.highlightLastMove) {
\r
4071 ClearHighlights();
\r
4072 DrawPosition(FALSE, NULL);
\r
4079 /* Pop up promotion dialog */
\r
4081 PromotionPopup(HWND hwnd)
\r
4085 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4086 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4087 hwnd, (DLGPROC)lpProc);
\r
4088 FreeProcInstance(lpProc);
\r
4094 DrawPosition(TRUE, NULL);
\r
4095 PromotionPopup(hwndMain);
\r
4098 /* Toggle ShowThinking */
\r
4100 ToggleShowThinking()
\r
4102 appData.showThinking = !appData.showThinking;
\r
4103 ShowThinkingEvent();
\r
4107 LoadGameDialog(HWND hwnd, char* title)
\r
4111 char fileTitle[MSG_SIZ];
\r
4112 f = OpenFileDialog(hwnd, "rb", "",
\r
4113 appData.oldSaveStyle ? "gam" : "pgn",
\r
4115 title, &number, fileTitle, NULL);
\r
4117 cmailMsgLoaded = FALSE;
\r
4118 if (number == 0) {
\r
4119 int error = GameListBuild(f);
\r
4121 DisplayError("Cannot build game list", error);
\r
4122 } else if (!ListEmpty(&gameList) &&
\r
4123 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4124 GameListPopUp(f, fileTitle);
\r
4127 GameListDestroy();
\r
4130 LoadGame(f, number, fileTitle, FALSE);
\r
4134 int get_term_width()
\r
4139 HFONT hfont, hold_font;
\r
4144 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4148 // get the text metrics
\r
4149 hdc = GetDC(hText);
\r
4150 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4151 if (consoleCF.dwEffects & CFE_BOLD)
\r
4152 lf.lfWeight = FW_BOLD;
\r
4153 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4154 lf.lfItalic = TRUE;
\r
4155 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4156 lf.lfStrikeOut = TRUE;
\r
4157 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4158 lf.lfUnderline = TRUE;
\r
4159 hfont = CreateFontIndirect(&lf);
\r
4160 hold_font = SelectObject(hdc, hfont);
\r
4161 GetTextMetrics(hdc, &tm);
\r
4162 SelectObject(hdc, hold_font);
\r
4163 DeleteObject(hfont);
\r
4164 ReleaseDC(hText, hdc);
\r
4166 // get the rectangle
\r
4167 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4169 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4172 void UpdateICSWidth(HWND hText)
\r
4174 LONG old_width, new_width;
\r
4176 new_width = get_term_width(hText, FALSE);
\r
4177 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4178 if (new_width != old_width)
\r
4180 ics_update_width(new_width);
\r
4181 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4186 ChangedConsoleFont()
\r
4189 CHARRANGE tmpsel, sel;
\r
4190 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4191 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4192 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4195 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4196 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4197 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4198 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4199 * size. This was undocumented in the version of MSVC++ that I had
\r
4200 * when I wrote the code, but is apparently documented now.
\r
4202 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4203 cfmt.bCharSet = f->lf.lfCharSet;
\r
4204 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4205 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4206 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4207 /* Why are the following seemingly needed too? */
\r
4208 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4209 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4210 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4212 tmpsel.cpMax = -1; /*999999?*/
\r
4213 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4214 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4215 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4216 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4218 paraf.cbSize = sizeof(paraf);
\r
4219 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4220 paraf.dxStartIndent = 0;
\r
4221 paraf.dxOffset = WRAP_INDENT;
\r
4222 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4223 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4224 UpdateICSWidth(hText);
\r
4227 /*---------------------------------------------------------------------------*\
\r
4229 * Window Proc for main window
\r
4231 \*---------------------------------------------------------------------------*/
\r
4233 /* Process messages for main window, etc. */
\r
4235 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4238 int wmId, wmEvent;
\r
4242 char fileTitle[MSG_SIZ];
\r
4243 char buf[MSG_SIZ];
\r
4244 static SnapData sd;
\r
4246 switch (message) {
\r
4248 case WM_PAINT: /* message: repaint portion of window */
\r
4252 case WM_ERASEBKGND:
\r
4253 if (IsIconic(hwnd)) {
\r
4254 /* Cheat; change the message */
\r
4255 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4257 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4261 case WM_LBUTTONDOWN:
\r
4262 case WM_MBUTTONDOWN:
\r
4263 case WM_RBUTTONDOWN:
\r
4264 case WM_LBUTTONUP:
\r
4265 case WM_MBUTTONUP:
\r
4266 case WM_RBUTTONUP:
\r
4267 case WM_MOUSEMOVE:
\r
4268 case WM_MOUSEWHEEL:
\r
4269 MouseEvent(hwnd, message, wParam, lParam);
\r
4272 JAWS_KB_NAVIGATION
\r
4276 JAWS_ALT_INTERCEPT
\r
4278 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4279 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4280 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4281 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4283 SendMessage(h, message, wParam, lParam);
\r
4284 } else if(lParam != KF_REPEAT) {
\r
4285 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4286 PopUpMoveDialog((char)wParam);
\r
4287 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4288 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4293 case WM_PALETTECHANGED:
\r
4294 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4296 HDC hdc = GetDC(hwndMain);
\r
4297 SelectPalette(hdc, hPal, TRUE);
\r
4298 nnew = RealizePalette(hdc);
\r
4300 paletteChanged = TRUE;
\r
4301 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4303 ReleaseDC(hwnd, hdc);
\r
4307 case WM_QUERYNEWPALETTE:
\r
4308 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4310 HDC hdc = GetDC(hwndMain);
\r
4311 paletteChanged = FALSE;
\r
4312 SelectPalette(hdc, hPal, FALSE);
\r
4313 nnew = RealizePalette(hdc);
\r
4315 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4317 ReleaseDC(hwnd, hdc);
\r
4322 case WM_COMMAND: /* message: command from application menu */
\r
4323 wmId = LOWORD(wParam);
\r
4324 wmEvent = HIWORD(wParam);
\r
4329 SAY("new game enter a move to play against the computer with white");
\r
4332 case IDM_NewGameFRC:
\r
4333 if( NewGameFRC() == 0 ) {
\r
4338 case IDM_NewVariant:
\r
4339 NewVariantPopup(hwnd);
\r
4342 case IDM_LoadGame:
\r
4343 LoadGameDialog(hwnd, "Load Game from File");
\r
4346 case IDM_LoadNextGame:
\r
4350 case IDM_LoadPrevGame:
\r
4354 case IDM_ReloadGame:
\r
4358 case IDM_LoadPosition:
\r
4359 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4360 Reset(FALSE, TRUE);
\r
4363 f = OpenFileDialog(hwnd, "rb", "",
\r
4364 appData.oldSaveStyle ? "pos" : "fen",
\r
4366 "Load Position from File", &number, fileTitle, NULL);
\r
4368 LoadPosition(f, number, fileTitle);
\r
4372 case IDM_LoadNextPosition:
\r
4373 ReloadPosition(1);
\r
4376 case IDM_LoadPrevPosition:
\r
4377 ReloadPosition(-1);
\r
4380 case IDM_ReloadPosition:
\r
4381 ReloadPosition(0);
\r
4384 case IDM_SaveGame:
\r
4385 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4386 f = OpenFileDialog(hwnd, "a", defName,
\r
4387 appData.oldSaveStyle ? "gam" : "pgn",
\r
4389 "Save Game to File", NULL, fileTitle, NULL);
\r
4391 SaveGame(f, 0, "");
\r
4395 case IDM_SavePosition:
\r
4396 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4397 f = OpenFileDialog(hwnd, "a", defName,
\r
4398 appData.oldSaveStyle ? "pos" : "fen",
\r
4400 "Save Position to File", NULL, fileTitle, NULL);
\r
4402 SavePosition(f, 0, "");
\r
4406 case IDM_SaveDiagram:
\r
4407 defName = "diagram";
\r
4408 f = OpenFileDialog(hwnd, "wb", defName,
\r
4411 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4417 case IDM_CopyGame:
\r
4418 CopyGameToClipboard();
\r
4421 case IDM_PasteGame:
\r
4422 PasteGameFromClipboard();
\r
4425 case IDM_CopyGameListToClipboard:
\r
4426 CopyGameListToClipboard();
\r
4429 /* [AS] Autodetect FEN or PGN data */
\r
4430 case IDM_PasteAny:
\r
4431 PasteGameOrFENFromClipboard();
\r
4434 /* [AS] Move history */
\r
4435 case IDM_ShowMoveHistory:
\r
4436 if( MoveHistoryIsUp() ) {
\r
4437 MoveHistoryPopDown();
\r
4440 MoveHistoryPopUp();
\r
4444 /* [AS] Eval graph */
\r
4445 case IDM_ShowEvalGraph:
\r
4446 if( EvalGraphIsUp() ) {
\r
4447 EvalGraphPopDown();
\r
4451 SetFocus(hwndMain);
\r
4455 /* [AS] Engine output */
\r
4456 case IDM_ShowEngineOutput:
\r
4457 if( EngineOutputIsUp() ) {
\r
4458 EngineOutputPopDown();
\r
4461 EngineOutputPopUp();
\r
4465 /* [AS] User adjudication */
\r
4466 case IDM_UserAdjudication_White:
\r
4467 UserAdjudicationEvent( +1 );
\r
4470 case IDM_UserAdjudication_Black:
\r
4471 UserAdjudicationEvent( -1 );
\r
4474 case IDM_UserAdjudication_Draw:
\r
4475 UserAdjudicationEvent( 0 );
\r
4478 /* [AS] Game list options dialog */
\r
4479 case IDM_GameListOptions:
\r
4480 GameListOptions();
\r
4487 case IDM_CopyPosition:
\r
4488 CopyFENToClipboard();
\r
4491 case IDM_PastePosition:
\r
4492 PasteFENFromClipboard();
\r
4495 case IDM_MailMove:
\r
4499 case IDM_ReloadCMailMsg:
\r
4500 Reset(TRUE, TRUE);
\r
4501 ReloadCmailMsgEvent(FALSE);
\r
4504 case IDM_Minimize:
\r
4505 ShowWindow(hwnd, SW_MINIMIZE);
\r
4512 case IDM_MachineWhite:
\r
4513 MachineWhiteEvent();
\r
4515 * refresh the tags dialog only if it's visible
\r
4517 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4519 tags = PGNTags(&gameInfo);
\r
4520 TagsPopUp(tags, CmailMsg());
\r
4523 SAY("computer starts playing white");
\r
4526 case IDM_MachineBlack:
\r
4527 MachineBlackEvent();
\r
4529 * refresh the tags dialog only if it's visible
\r
4531 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4533 tags = PGNTags(&gameInfo);
\r
4534 TagsPopUp(tags, CmailMsg());
\r
4537 SAY("computer starts playing black");
\r
4540 case IDM_TwoMachines:
\r
4541 TwoMachinesEvent();
\r
4543 * refresh the tags dialog only if it's visible
\r
4545 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4547 tags = PGNTags(&gameInfo);
\r
4548 TagsPopUp(tags, CmailMsg());
\r
4551 SAY("programs start playing each other");
\r
4554 case IDM_AnalysisMode:
\r
4555 if (!first.analysisSupport) {
\r
4556 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4557 DisplayError(buf, 0);
\r
4559 SAY("analyzing current position");
\r
4560 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4561 if (appData.icsActive) {
\r
4562 if (gameMode != IcsObserving) {
\r
4563 sprintf(buf, "You are not observing a game");
\r
4564 DisplayError(buf, 0);
\r
4565 /* secure check */
\r
4566 if (appData.icsEngineAnalyze) {
\r
4567 if (appData.debugMode)
\r
4568 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4569 ExitAnalyzeMode();
\r
4575 /* if enable, user want disable icsEngineAnalyze */
\r
4576 if (appData.icsEngineAnalyze) {
\r
4577 ExitAnalyzeMode();
\r
4581 appData.icsEngineAnalyze = TRUE;
\r
4582 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4585 if (!appData.showThinking) ToggleShowThinking();
\r
4586 AnalyzeModeEvent();
\r
4590 case IDM_AnalyzeFile:
\r
4591 if (!first.analysisSupport) {
\r
4592 char buf[MSG_SIZ];
\r
4593 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4594 DisplayError(buf, 0);
\r
4596 if (!appData.showThinking) ToggleShowThinking();
\r
4597 AnalyzeFileEvent();
\r
4598 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4599 AnalysisPeriodicEvent(1);
\r
4603 case IDM_IcsClient:
\r
4607 case IDM_EditGame:
\r
4612 case IDM_EditPosition:
\r
4613 EditPositionEvent();
\r
4614 SAY("to set up a position type a FEN");
\r
4617 case IDM_Training:
\r
4621 case IDM_ShowGameList:
\r
4622 ShowGameListProc();
\r
4625 case IDM_EditTags:
\r
4629 case IDM_EditComment:
\r
4630 if (commentUp && editComment) {
\r
4633 EditCommentEvent();
\r
4653 case IDM_CallFlag:
\r
4673 case IDM_StopObserving:
\r
4674 StopObservingEvent();
\r
4677 case IDM_StopExamining:
\r
4678 StopExaminingEvent();
\r
4682 UploadGameEvent();
\r
4685 case IDM_TypeInMove:
\r
4686 PopUpMoveDialog('\000');
\r
4689 case IDM_TypeInName:
\r
4690 PopUpNameDialog('\000');
\r
4693 case IDM_Backward:
\r
4695 SetFocus(hwndMain);
\r
4702 SetFocus(hwndMain);
\r
4707 SetFocus(hwndMain);
\r
4712 SetFocus(hwndMain);
\r
4716 RevertEvent(FALSE);
\r
4719 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4720 RevertEvent(TRUE);
\r
4723 case IDM_TruncateGame:
\r
4724 TruncateGameEvent();
\r
4731 case IDM_RetractMove:
\r
4732 RetractMoveEvent();
\r
4735 case IDM_FlipView:
\r
4736 flipView = !flipView;
\r
4737 DrawPosition(FALSE, NULL);
\r
4740 case IDM_FlipClock:
\r
4741 flipClock = !flipClock;
\r
4742 DisplayBothClocks();
\r
4743 DrawPosition(FALSE, NULL);
\r
4746 case IDM_MuteSounds:
\r
4747 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4748 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4749 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4752 case IDM_GeneralOptions:
\r
4753 GeneralOptionsPopup(hwnd);
\r
4754 DrawPosition(TRUE, NULL);
\r
4757 case IDM_BoardOptions:
\r
4758 BoardOptionsPopup(hwnd);
\r
4761 case IDM_EnginePlayOptions:
\r
4762 EnginePlayOptionsPopup(hwnd);
\r
4765 case IDM_Engine1Options:
\r
4766 EngineOptionsPopup(hwnd, &first);
\r
4769 case IDM_Engine2Options:
\r
4770 EngineOptionsPopup(hwnd, &second);
\r
4773 case IDM_OptionsUCI:
\r
4774 UciOptionsPopup(hwnd);
\r
4777 case IDM_IcsOptions:
\r
4778 IcsOptionsPopup(hwnd);
\r
4782 FontsOptionsPopup(hwnd);
\r
4786 SoundOptionsPopup(hwnd);
\r
4789 case IDM_CommPort:
\r
4790 CommPortOptionsPopup(hwnd);
\r
4793 case IDM_LoadOptions:
\r
4794 LoadOptionsPopup(hwnd);
\r
4797 case IDM_SaveOptions:
\r
4798 SaveOptionsPopup(hwnd);
\r
4801 case IDM_TimeControl:
\r
4802 TimeControlOptionsPopup(hwnd);
\r
4805 case IDM_SaveSettings:
\r
4806 SaveSettings(settingsFileName);
\r
4809 case IDM_SaveSettingsOnExit:
\r
4810 saveSettingsOnExit = !saveSettingsOnExit;
\r
4811 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4812 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4813 MF_CHECKED : MF_UNCHECKED));
\r
4824 case IDM_AboutGame:
\r
4829 appData.debugMode = !appData.debugMode;
\r
4830 if (appData.debugMode) {
\r
4831 char dir[MSG_SIZ];
\r
4832 GetCurrentDirectory(MSG_SIZ, dir);
\r
4833 SetCurrentDirectory(installDir);
\r
4834 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4835 SetCurrentDirectory(dir);
\r
4836 setbuf(debugFP, NULL);
\r
4843 case IDM_HELPCONTENTS:
\r
4844 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4845 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4846 MessageBox (GetFocus(),
\r
4847 "Unable to activate help",
\r
4848 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4852 case IDM_HELPSEARCH:
\r
4853 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4854 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4855 MessageBox (GetFocus(),
\r
4856 "Unable to activate help",
\r
4857 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4861 case IDM_HELPHELP:
\r
4862 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4863 MessageBox (GetFocus(),
\r
4864 "Unable to activate help",
\r
4865 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4870 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4872 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4873 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4874 FreeProcInstance(lpProc);
\r
4877 case IDM_DirectCommand1:
\r
4878 AskQuestionEvent("Direct Command",
\r
4879 "Send to chess program:", "", "1");
\r
4881 case IDM_DirectCommand2:
\r
4882 AskQuestionEvent("Direct Command",
\r
4883 "Send to second chess program:", "", "2");
\r
4886 case EP_WhitePawn:
\r
4887 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4888 fromX = fromY = -1;
\r
4891 case EP_WhiteKnight:
\r
4892 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4893 fromX = fromY = -1;
\r
4896 case EP_WhiteBishop:
\r
4897 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4898 fromX = fromY = -1;
\r
4901 case EP_WhiteRook:
\r
4902 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4903 fromX = fromY = -1;
\r
4906 case EP_WhiteQueen:
\r
4907 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4908 fromX = fromY = -1;
\r
4911 case EP_WhiteFerz:
\r
4912 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4913 fromX = fromY = -1;
\r
4916 case EP_WhiteWazir:
\r
4917 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4918 fromX = fromY = -1;
\r
4921 case EP_WhiteAlfil:
\r
4922 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4923 fromX = fromY = -1;
\r
4926 case EP_WhiteCannon:
\r
4927 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4928 fromX = fromY = -1;
\r
4931 case EP_WhiteCardinal:
\r
4932 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4933 fromX = fromY = -1;
\r
4936 case EP_WhiteMarshall:
\r
4937 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4938 fromX = fromY = -1;
\r
4941 case EP_WhiteKing:
\r
4942 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4943 fromX = fromY = -1;
\r
4946 case EP_BlackPawn:
\r
4947 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4948 fromX = fromY = -1;
\r
4951 case EP_BlackKnight:
\r
4952 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4953 fromX = fromY = -1;
\r
4956 case EP_BlackBishop:
\r
4957 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4958 fromX = fromY = -1;
\r
4961 case EP_BlackRook:
\r
4962 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4963 fromX = fromY = -1;
\r
4966 case EP_BlackQueen:
\r
4967 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4968 fromX = fromY = -1;
\r
4971 case EP_BlackFerz:
\r
4972 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4973 fromX = fromY = -1;
\r
4976 case EP_BlackWazir:
\r
4977 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4978 fromX = fromY = -1;
\r
4981 case EP_BlackAlfil:
\r
4982 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4983 fromX = fromY = -1;
\r
4986 case EP_BlackCannon:
\r
4987 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4988 fromX = fromY = -1;
\r
4991 case EP_BlackCardinal:
\r
4992 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4993 fromX = fromY = -1;
\r
4996 case EP_BlackMarshall:
\r
4997 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4998 fromX = fromY = -1;
\r
5001 case EP_BlackKing:
\r
5002 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5003 fromX = fromY = -1;
\r
5006 case EP_EmptySquare:
\r
5007 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5008 fromX = fromY = -1;
\r
5011 case EP_ClearBoard:
\r
5012 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5013 fromX = fromY = -1;
\r
5017 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5018 fromX = fromY = -1;
\r
5022 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5023 fromX = fromY = -1;
\r
5027 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5028 fromX = fromY = -1;
\r
5032 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5033 fromX = fromY = -1;
\r
5037 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5038 fromX = fromY = -1;
\r
5042 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5043 fromX = fromY = -1;
\r
5047 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5048 fromX = fromY = -1;
\r
5052 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5053 fromX = fromY = -1;
\r
5057 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5058 fromX = fromY = -1;
\r
5062 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5068 case CLOCK_TIMER_ID:
\r
5069 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5070 clockTimerEvent = 0;
\r
5071 DecrementClocks(); /* call into back end */
\r
5073 case LOAD_GAME_TIMER_ID:
\r
5074 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5075 loadGameTimerEvent = 0;
\r
5076 AutoPlayGameLoop(); /* call into back end */
\r
5078 case ANALYSIS_TIMER_ID:
\r
5079 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5080 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5081 AnalysisPeriodicEvent(0);
\r
5083 KillTimer(hwnd, analysisTimerEvent);
\r
5084 analysisTimerEvent = 0;
\r
5087 case DELAYED_TIMER_ID:
\r
5088 KillTimer(hwnd, delayedTimerEvent);
\r
5089 delayedTimerEvent = 0;
\r
5090 delayedTimerCallback();
\r
5095 case WM_USER_Input:
\r
5096 InputEvent(hwnd, message, wParam, lParam);
\r
5099 /* [AS] Also move "attached" child windows */
\r
5100 case WM_WINDOWPOSCHANGING:
\r
5102 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5103 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5105 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5106 /* Window is moving */
\r
5109 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5110 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5111 rcMain.right = wpMain.x + wpMain.width;
\r
5112 rcMain.top = wpMain.y;
\r
5113 rcMain.bottom = wpMain.y + wpMain.height;
\r
5115 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5116 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5117 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5118 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5119 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5120 wpMain.x = lpwp->x;
\r
5121 wpMain.y = lpwp->y;
\r
5126 /* [AS] Snapping */
\r
5127 case WM_ENTERSIZEMOVE:
\r
5128 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5129 if (hwnd == hwndMain) {
\r
5130 doingSizing = TRUE;
\r
5133 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5137 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5138 if (hwnd == hwndMain) {
\r
5139 lastSizing = wParam;
\r
5144 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5145 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5147 case WM_EXITSIZEMOVE:
\r
5148 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5149 if (hwnd == hwndMain) {
\r
5151 doingSizing = FALSE;
\r
5152 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5153 GetClientRect(hwnd, &client);
\r
5154 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5156 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5158 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5161 case WM_DESTROY: /* message: window being destroyed */
\r
5162 PostQuitMessage(0);
\r
5166 if (hwnd == hwndMain) {
\r
5171 default: /* Passes it on if unprocessed */
\r
5172 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5177 /*---------------------------------------------------------------------------*\
\r
5179 * Misc utility routines
\r
5181 \*---------------------------------------------------------------------------*/
\r
5184 * Decent random number generator, at least not as bad as Windows
\r
5185 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5187 unsigned int randstate;
\r
5192 randstate = randstate * 1664525 + 1013904223;
\r
5193 return (int) randstate & 0x7fffffff;
\r
5197 mysrandom(unsigned int seed)
\r
5204 * returns TRUE if user selects a different color, FALSE otherwise
\r
5208 ChangeColor(HWND hwnd, COLORREF *which)
\r
5210 static BOOL firstTime = TRUE;
\r
5211 static DWORD customColors[16];
\r
5213 COLORREF newcolor;
\r
5218 /* Make initial colors in use available as custom colors */
\r
5219 /* Should we put the compiled-in defaults here instead? */
\r
5221 customColors[i++] = lightSquareColor & 0xffffff;
\r
5222 customColors[i++] = darkSquareColor & 0xffffff;
\r
5223 customColors[i++] = whitePieceColor & 0xffffff;
\r
5224 customColors[i++] = blackPieceColor & 0xffffff;
\r
5225 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5226 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5228 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5229 customColors[i++] = textAttribs[ccl].color;
\r
5231 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5232 firstTime = FALSE;
\r
5235 cc.lStructSize = sizeof(cc);
\r
5236 cc.hwndOwner = hwnd;
\r
5237 cc.hInstance = NULL;
\r
5238 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5239 cc.lpCustColors = (LPDWORD) customColors;
\r
5240 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5242 if (!ChooseColor(&cc)) return FALSE;
\r
5244 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5245 if (newcolor == *which) return FALSE;
\r
5246 *which = newcolor;
\r
5250 InitDrawingColors();
\r
5251 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5256 MyLoadSound(MySound *ms)
\r
5262 if (ms->data) free(ms->data);
\r
5265 switch (ms->name[0]) {
\r
5271 /* System sound from Control Panel. Don't preload here. */
\r
5275 if (ms->name[1] == NULLCHAR) {
\r
5276 /* "!" alone = silence */
\r
5279 /* Builtin wave resource. Error if not found. */
\r
5280 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5281 if (h == NULL) break;
\r
5282 ms->data = (void *)LoadResource(hInst, h);
\r
5283 if (h == NULL) break;
\r
5288 /* .wav file. Error if not found. */
\r
5289 f = fopen(ms->name, "rb");
\r
5290 if (f == NULL) break;
\r
5291 if (fstat(fileno(f), &st) < 0) break;
\r
5292 ms->data = malloc(st.st_size);
\r
5293 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5299 char buf[MSG_SIZ];
\r
5300 sprintf(buf, "Error loading sound %s", ms->name);
\r
5301 DisplayError(buf, GetLastError());
\r
5307 MyPlaySound(MySound *ms)
\r
5309 BOOLEAN ok = FALSE;
\r
5311 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5312 switch (ms->name[0]) {
\r
5314 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5319 /* System sound from Control Panel (deprecated feature).
\r
5320 "$" alone or an unset sound name gets default beep (still in use). */
\r
5321 if (ms->name[1]) {
\r
5322 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5324 if (!ok) ok = MessageBeep(MB_OK);
\r
5327 /* Builtin wave resource, or "!" alone for silence */
\r
5328 if (ms->name[1]) {
\r
5329 if (ms->data == NULL) return FALSE;
\r
5330 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5336 /* .wav file. Error if not found. */
\r
5337 if (ms->data == NULL) return FALSE;
\r
5338 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5341 /* Don't print an error: this can happen innocently if the sound driver
\r
5342 is busy; for instance, if another instance of WinBoard is playing
\r
5343 a sound at about the same time. */
\r
5349 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5352 OPENFILENAME *ofn;
\r
5353 static UINT *number; /* gross that this is static */
\r
5355 switch (message) {
\r
5356 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5357 /* Center the dialog over the application window */
\r
5358 ofn = (OPENFILENAME *) lParam;
\r
5359 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5360 number = (UINT *) ofn->lCustData;
\r
5361 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5365 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5366 return FALSE; /* Allow for further processing */
\r
5369 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5370 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5372 return FALSE; /* Allow for further processing */
\r
5378 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5380 static UINT *number;
\r
5381 OPENFILENAME *ofname;
\r
5384 case WM_INITDIALOG:
\r
5385 ofname = (OPENFILENAME *)lParam;
\r
5386 number = (UINT *)(ofname->lCustData);
\r
5389 ofnot = (OFNOTIFY *)lParam;
\r
5390 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5391 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5400 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5401 char *nameFilt, char *dlgTitle, UINT *number,
\r
5402 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5404 OPENFILENAME openFileName;
\r
5405 char buf1[MSG_SIZ];
\r
5408 if (fileName == NULL) fileName = buf1;
\r
5409 if (defName == NULL) {
\r
5410 strcpy(fileName, "*.");
\r
5411 strcat(fileName, defExt);
\r
5413 strcpy(fileName, defName);
\r
5415 if (fileTitle) strcpy(fileTitle, "");
\r
5416 if (number) *number = 0;
\r
5418 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5419 openFileName.hwndOwner = hwnd;
\r
5420 openFileName.hInstance = (HANDLE) hInst;
\r
5421 openFileName.lpstrFilter = nameFilt;
\r
5422 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5423 openFileName.nMaxCustFilter = 0L;
\r
5424 openFileName.nFilterIndex = 1L;
\r
5425 openFileName.lpstrFile = fileName;
\r
5426 openFileName.nMaxFile = MSG_SIZ;
\r
5427 openFileName.lpstrFileTitle = fileTitle;
\r
5428 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5429 openFileName.lpstrInitialDir = NULL;
\r
5430 openFileName.lpstrTitle = dlgTitle;
\r
5431 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5432 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5433 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5434 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5435 openFileName.nFileOffset = 0;
\r
5436 openFileName.nFileExtension = 0;
\r
5437 openFileName.lpstrDefExt = defExt;
\r
5438 openFileName.lCustData = (LONG) number;
\r
5439 openFileName.lpfnHook = oldDialog ?
\r
5440 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5441 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5443 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5444 GetOpenFileName(&openFileName)) {
\r
5445 /* open the file */
\r
5446 f = fopen(openFileName.lpstrFile, write);
\r
5448 MessageBox(hwnd, "File open failed", NULL,
\r
5449 MB_OK|MB_ICONEXCLAMATION);
\r
5453 int err = CommDlgExtendedError();
\r
5454 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5463 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5465 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5468 * Get the first pop-up menu in the menu template. This is the
\r
5469 * menu that TrackPopupMenu displays.
\r
5471 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5473 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5476 * TrackPopup uses screen coordinates, so convert the
\r
5477 * coordinates of the mouse click to screen coordinates.
\r
5479 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5481 /* Draw and track the floating pop-up menu. */
\r
5482 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5483 pt.x, pt.y, 0, hwnd, NULL);
\r
5485 /* Destroy the menu.*/
\r
5486 DestroyMenu(hmenu);
\r
5491 int sizeX, sizeY, newSizeX, newSizeY;
\r
5493 } ResizeEditPlusButtonsClosure;
\r
5496 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5498 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5502 if (hChild == cl->hText) return TRUE;
\r
5503 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5504 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5505 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5506 ScreenToClient(cl->hDlg, &pt);
\r
5507 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5508 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5512 /* Resize a dialog that has a (rich) edit field filling most of
\r
5513 the top, with a row of buttons below */
\r
5515 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5518 int newTextHeight, newTextWidth;
\r
5519 ResizeEditPlusButtonsClosure cl;
\r
5521 /*if (IsIconic(hDlg)) return;*/
\r
5522 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5524 cl.hdwp = BeginDeferWindowPos(8);
\r
5526 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5527 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5528 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5529 if (newTextHeight < 0) {
\r
5530 newSizeY += -newTextHeight;
\r
5531 newTextHeight = 0;
\r
5533 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5534 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5540 cl.newSizeX = newSizeX;
\r
5541 cl.newSizeY = newSizeY;
\r
5542 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5544 EndDeferWindowPos(cl.hdwp);
\r
5547 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5549 RECT rChild, rParent;
\r
5550 int wChild, hChild, wParent, hParent;
\r
5551 int wScreen, hScreen, xNew, yNew;
\r
5554 /* Get the Height and Width of the child window */
\r
5555 GetWindowRect (hwndChild, &rChild);
\r
5556 wChild = rChild.right - rChild.left;
\r
5557 hChild = rChild.bottom - rChild.top;
\r
5559 /* Get the Height and Width of the parent window */
\r
5560 GetWindowRect (hwndParent, &rParent);
\r
5561 wParent = rParent.right - rParent.left;
\r
5562 hParent = rParent.bottom - rParent.top;
\r
5564 /* Get the display limits */
\r
5565 hdc = GetDC (hwndChild);
\r
5566 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5567 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5568 ReleaseDC(hwndChild, hdc);
\r
5570 /* Calculate new X position, then adjust for screen */
\r
5571 xNew = rParent.left + ((wParent - wChild) /2);
\r
5574 } else if ((xNew+wChild) > wScreen) {
\r
5575 xNew = wScreen - wChild;
\r
5578 /* Calculate new Y position, then adjust for screen */
\r
5580 yNew = rParent.top + ((hParent - hChild) /2);
\r
5583 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5588 } else if ((yNew+hChild) > hScreen) {
\r
5589 yNew = hScreen - hChild;
\r
5592 /* Set it, and return */
\r
5593 return SetWindowPos (hwndChild, NULL,
\r
5594 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5597 /* Center one window over another */
\r
5598 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5600 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5603 /*---------------------------------------------------------------------------*\
\r
5605 * Startup Dialog functions
\r
5607 \*---------------------------------------------------------------------------*/
\r
5609 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5611 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5613 while (*cd != NULL) {
\r
5614 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5620 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5622 char buf1[MAX_ARG_LEN];
\r
5625 if (str[0] == '@') {
\r
5626 FILE* f = fopen(str + 1, "r");
\r
5628 DisplayFatalError(str + 1, errno, 2);
\r
5631 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5633 buf1[len] = NULLCHAR;
\r
5637 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5640 char buf[MSG_SIZ];
\r
5641 char *end = strchr(str, '\n');
\r
5642 if (end == NULL) return;
\r
5643 memcpy(buf, str, end - str);
\r
5644 buf[end - str] = NULLCHAR;
\r
5645 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5651 SetStartupDialogEnables(HWND hDlg)
\r
5653 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5654 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5655 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5656 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5657 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5658 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5659 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5660 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5661 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5662 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5663 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5664 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5665 IsDlgButtonChecked(hDlg, OPT_View));
\r
5669 QuoteForFilename(char *filename)
\r
5671 int dquote, space;
\r
5672 dquote = strchr(filename, '"') != NULL;
\r
5673 space = strchr(filename, ' ') != NULL;
\r
5674 if (dquote || space) {
\r
5686 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5688 char buf[MSG_SIZ];
\r
5691 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5692 q = QuoteForFilename(nthcp);
\r
5693 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5694 if (*nthdir != NULLCHAR) {
\r
5695 q = QuoteForFilename(nthdir);
\r
5696 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5698 if (*nthcp == NULLCHAR) {
\r
5699 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5700 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5701 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5702 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5707 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5709 char buf[MSG_SIZ];
\r
5713 switch (message) {
\r
5714 case WM_INITDIALOG:
\r
5715 /* Center the dialog */
\r
5716 CenterWindow (hDlg, GetDesktopWindow());
\r
5717 /* Initialize the dialog items */
\r
5718 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5719 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5720 firstChessProgramNames);
\r
5721 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5722 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5723 secondChessProgramNames);
\r
5724 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5725 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5726 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5727 if (*appData.icsHelper != NULLCHAR) {
\r
5728 char *q = QuoteForFilename(appData.icsHelper);
\r
5729 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5731 if (*appData.icsHost == NULLCHAR) {
\r
5732 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5733 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5734 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5735 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5736 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5739 if (appData.icsActive) {
\r
5740 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5742 else if (appData.noChessProgram) {
\r
5743 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5746 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5749 SetStartupDialogEnables(hDlg);
\r
5753 switch (LOWORD(wParam)) {
\r
5755 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5756 strcpy(buf, "/fcp=");
\r
5757 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5759 ParseArgs(StringGet, &p);
\r
5760 strcpy(buf, "/scp=");
\r
5761 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5763 ParseArgs(StringGet, &p);
\r
5764 appData.noChessProgram = FALSE;
\r
5765 appData.icsActive = FALSE;
\r
5766 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5767 strcpy(buf, "/ics /icshost=");
\r
5768 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5770 ParseArgs(StringGet, &p);
\r
5771 if (appData.zippyPlay) {
\r
5772 strcpy(buf, "/fcp=");
\r
5773 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5775 ParseArgs(StringGet, &p);
\r
5777 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5778 appData.noChessProgram = TRUE;
\r
5779 appData.icsActive = FALSE;
\r
5781 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5782 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5785 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5786 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5788 ParseArgs(StringGet, &p);
\r
5790 EndDialog(hDlg, TRUE);
\r
5797 case IDM_HELPCONTENTS:
\r
5798 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5799 MessageBox (GetFocus(),
\r
5800 "Unable to activate help",
\r
5801 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5806 SetStartupDialogEnables(hDlg);
\r
5814 /*---------------------------------------------------------------------------*\
\r
5816 * About box dialog functions
\r
5818 \*---------------------------------------------------------------------------*/
\r
5820 /* Process messages for "About" dialog box */
\r
5822 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5824 switch (message) {
\r
5825 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5826 /* Center the dialog over the application window */
\r
5827 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5828 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5832 case WM_COMMAND: /* message: received a command */
\r
5833 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5834 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5835 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5843 /*---------------------------------------------------------------------------*\
\r
5845 * Comment Dialog functions
\r
5847 \*---------------------------------------------------------------------------*/
\r
5850 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5852 static HANDLE hwndText = NULL;
\r
5853 int len, newSizeX, newSizeY, flags;
\r
5854 static int sizeX, sizeY;
\r
5859 switch (message) {
\r
5860 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5861 /* Initialize the dialog items */
\r
5862 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5863 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5864 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5865 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5866 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5867 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5868 SetWindowText(hDlg, commentTitle);
\r
5869 if (editComment) {
\r
5870 SetFocus(hwndText);
\r
5872 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5874 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5875 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5876 MAKELPARAM(FALSE, 0));
\r
5877 /* Size and position the dialog */
\r
5878 if (!commentDialog) {
\r
5879 commentDialog = hDlg;
\r
5880 flags = SWP_NOZORDER;
\r
5881 GetClientRect(hDlg, &rect);
\r
5882 sizeX = rect.right;
\r
5883 sizeY = rect.bottom;
\r
5884 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5885 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5886 WINDOWPLACEMENT wp;
\r
5887 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5888 wp.length = sizeof(WINDOWPLACEMENT);
\r
5890 wp.showCmd = SW_SHOW;
\r
5891 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5892 wp.rcNormalPosition.left = wpComment.x;
\r
5893 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5894 wp.rcNormalPosition.top = wpComment.y;
\r
5895 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5896 SetWindowPlacement(hDlg, &wp);
\r
5898 GetClientRect(hDlg, &rect);
\r
5899 newSizeX = rect.right;
\r
5900 newSizeY = rect.bottom;
\r
5901 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5902 newSizeX, newSizeY);
\r
5907 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5910 case WM_COMMAND: /* message: received a command */
\r
5911 switch (LOWORD(wParam)) {
\r
5913 if (editComment) {
\r
5915 /* Read changed options from the dialog box */
\r
5916 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5917 len = GetWindowTextLength(hwndText);
\r
5918 str = (char *) malloc(len + 1);
\r
5919 GetWindowText(hwndText, str, len + 1);
\r
5928 ReplaceComment(commentIndex, str);
\r
5935 case OPT_CancelComment:
\r
5939 case OPT_ClearComment:
\r
5940 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5943 case OPT_EditComment:
\r
5944 EditCommentEvent();
\r
5952 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5953 if( wParam == OPT_CommentText ) {
\r
5954 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5956 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5960 pt.x = LOWORD( lpMF->lParam );
\r
5961 pt.y = HIWORD( lpMF->lParam );
\r
5963 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5965 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5966 len = GetWindowTextLength(hwndText);
\r
5967 str = (char *) malloc(len + 1);
\r
5968 GetWindowText(hwndText, str, len + 1);
\r
5969 ReplaceComment(commentIndex, str);
\r
5970 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5971 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5974 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5975 lpMF->msg = WM_USER;
\r
5983 newSizeX = LOWORD(lParam);
\r
5984 newSizeY = HIWORD(lParam);
\r
5985 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5990 case WM_GETMINMAXINFO:
\r
5991 /* Prevent resizing window too small */
\r
5992 mmi = (MINMAXINFO *) lParam;
\r
5993 mmi->ptMinTrackSize.x = 100;
\r
5994 mmi->ptMinTrackSize.y = 100;
\r
6001 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6006 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6008 if (str == NULL) str = "";
\r
6009 p = (char *) malloc(2 * strlen(str) + 2);
\r
6012 if (*str == '\n') *q++ = '\r';
\r
6016 if (commentText != NULL) free(commentText);
\r
6018 commentIndex = index;
\r
6019 commentTitle = title;
\r
6021 editComment = edit;
\r
6023 if (commentDialog) {
\r
6024 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6025 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6027 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6028 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6029 hwndMain, (DLGPROC)lpProc);
\r
6030 FreeProcInstance(lpProc);
\r
6036 /*---------------------------------------------------------------------------*\
\r
6038 * Type-in move dialog functions
\r
6040 \*---------------------------------------------------------------------------*/
\r
6043 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6045 char move[MSG_SIZ];
\r
6047 ChessMove moveType;
\r
6048 int fromX, fromY, toX, toY;
\r
6051 switch (message) {
\r
6052 case WM_INITDIALOG:
\r
6053 move[0] = (char) lParam;
\r
6054 move[1] = NULLCHAR;
\r
6055 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6056 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6057 SetWindowText(hInput, move);
\r
6059 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6063 switch (LOWORD(wParam)) {
\r
6065 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6066 { int n; Board board;
\r
6068 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6069 EditPositionPasteFEN(move);
\r
6070 EndDialog(hDlg, TRUE);
\r
6073 // [HGM] movenum: allow move number to be typed in any mode
\r
6074 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6076 EndDialog(hDlg, TRUE);
\r
6080 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6081 gameMode != Training) {
\r
6082 DisplayMoveError("Displayed move is not current");
\r
6084 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6085 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6086 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6087 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6088 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6089 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6090 if (gameMode != Training)
\r
6091 forwardMostMove = currentMove;
\r
6092 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6094 DisplayMoveError("Could not parse move");
\r
6097 EndDialog(hDlg, TRUE);
\r
6100 EndDialog(hDlg, FALSE);
\r
6111 PopUpMoveDialog(char firstchar)
\r
6115 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6116 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6117 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6118 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6119 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6120 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6121 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6122 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6123 gameMode == Training) {
\r
6124 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6125 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6126 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6127 FreeProcInstance(lpProc);
\r
6131 /*---------------------------------------------------------------------------*\
\r
6133 * Type-in name dialog functions
\r
6135 \*---------------------------------------------------------------------------*/
\r
6138 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6140 char move[MSG_SIZ];
\r
6143 switch (message) {
\r
6144 case WM_INITDIALOG:
\r
6145 move[0] = (char) lParam;
\r
6146 move[1] = NULLCHAR;
\r
6147 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6148 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6149 SetWindowText(hInput, move);
\r
6151 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6155 switch (LOWORD(wParam)) {
\r
6157 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6158 appData.userName = strdup(move);
\r
6161 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6162 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6163 DisplayTitle(move);
\r
6167 EndDialog(hDlg, TRUE);
\r
6170 EndDialog(hDlg, FALSE);
\r
6181 PopUpNameDialog(char firstchar)
\r
6185 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6186 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6187 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6188 FreeProcInstance(lpProc);
\r
6191 /*---------------------------------------------------------------------------*\
\r
6195 \*---------------------------------------------------------------------------*/
\r
6197 /* Nonmodal error box */
\r
6198 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6199 WPARAM wParam, LPARAM lParam);
\r
6202 ErrorPopUp(char *title, char *content)
\r
6206 BOOLEAN modal = hwndMain == NULL;
\r
6224 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6225 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6228 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6230 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6231 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6232 hwndMain, (DLGPROC)lpProc);
\r
6233 FreeProcInstance(lpProc);
\r
6240 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6241 if (errorDialog == NULL) return;
\r
6242 DestroyWindow(errorDialog);
\r
6243 errorDialog = NULL;
\r
6244 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6248 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6253 switch (message) {
\r
6254 case WM_INITDIALOG:
\r
6255 GetWindowRect(hDlg, &rChild);
\r
6258 SetWindowPos(hDlg, NULL, rChild.left,
\r
6259 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6260 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6264 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6265 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6266 and it doesn't work when you resize the dialog.
\r
6267 For now, just give it a default position.
\r
6269 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6271 errorDialog = hDlg;
\r
6272 SetWindowText(hDlg, errorTitle);
\r
6273 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6274 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6278 switch (LOWORD(wParam)) {
\r
6281 if (errorDialog == hDlg) errorDialog = NULL;
\r
6282 DestroyWindow(hDlg);
\r
6294 HWND gothicDialog = NULL;
\r
6297 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6301 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6303 switch (message) {
\r
6304 case WM_INITDIALOG:
\r
6305 GetWindowRect(hDlg, &rChild);
\r
6307 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6311 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6312 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6313 and it doesn't work when you resize the dialog.
\r
6314 For now, just give it a default position.
\r
6316 gothicDialog = hDlg;
\r
6317 SetWindowText(hDlg, errorTitle);
\r
6318 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6319 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6323 switch (LOWORD(wParam)) {
\r
6326 if (errorDialog == hDlg) errorDialog = NULL;
\r
6327 DestroyWindow(hDlg);
\r
6339 GothicPopUp(char *title, VariantClass variant)
\r
6342 static char *lastTitle;
\r
6344 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6345 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6347 if(lastTitle != title && gothicDialog != NULL) {
\r
6348 DestroyWindow(gothicDialog);
\r
6349 gothicDialog = NULL;
\r
6351 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6352 title = lastTitle;
\r
6353 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6354 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6355 hwndMain, (DLGPROC)lpProc);
\r
6356 FreeProcInstance(lpProc);
\r
6361 /*---------------------------------------------------------------------------*\
\r
6363 * Ics Interaction console functions
\r
6365 \*---------------------------------------------------------------------------*/
\r
6367 #define HISTORY_SIZE 64
\r
6368 static char *history[HISTORY_SIZE];
\r
6369 int histIn = 0, histP = 0;
\r
6372 SaveInHistory(char *cmd)
\r
6374 if (history[histIn] != NULL) {
\r
6375 free(history[histIn]);
\r
6376 history[histIn] = NULL;
\r
6378 if (*cmd == NULLCHAR) return;
\r
6379 history[histIn] = StrSave(cmd);
\r
6380 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6381 if (history[histIn] != NULL) {
\r
6382 free(history[histIn]);
\r
6383 history[histIn] = NULL;
\r
6389 PrevInHistory(char *cmd)
\r
6392 if (histP == histIn) {
\r
6393 if (history[histIn] != NULL) free(history[histIn]);
\r
6394 history[histIn] = StrSave(cmd);
\r
6396 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6397 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6399 return history[histP];
\r
6405 if (histP == histIn) return NULL;
\r
6406 histP = (histP + 1) % HISTORY_SIZE;
\r
6407 return history[histP];
\r
6411 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6415 hmenu = LoadMenu(hInst, "TextMenu");
\r
6416 h = GetSubMenu(hmenu, 0);
\r
6418 if (strcmp(e->item, "-") == 0) {
\r
6419 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6420 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6421 int flags = MF_STRING, j = 0;
\r
6422 if (e->item[0] == '|') {
\r
6423 flags |= MF_MENUBARBREAK;
\r
6426 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6427 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6435 WNDPROC consoleTextWindowProc;
\r
6438 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6440 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6441 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6445 SetWindowText(hInput, command);
\r
6447 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6449 sel.cpMin = 999999;
\r
6450 sel.cpMax = 999999;
\r
6451 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6456 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6457 if (sel.cpMin == sel.cpMax) {
\r
6458 /* Expand to surrounding word */
\r
6461 tr.chrg.cpMax = sel.cpMin;
\r
6462 tr.chrg.cpMin = --sel.cpMin;
\r
6463 if (sel.cpMin < 0) break;
\r
6464 tr.lpstrText = name;
\r
6465 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6466 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6470 tr.chrg.cpMin = sel.cpMax;
\r
6471 tr.chrg.cpMax = ++sel.cpMax;
\r
6472 tr.lpstrText = name;
\r
6473 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6474 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6477 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6478 MessageBeep(MB_ICONEXCLAMATION);
\r
6482 tr.lpstrText = name;
\r
6483 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6485 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6486 MessageBeep(MB_ICONEXCLAMATION);
\r
6489 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6492 sprintf(buf, "%s %s", command, name);
\r
6493 SetWindowText(hInput, buf);
\r
6494 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6496 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6497 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6498 SetWindowText(hInput, buf);
\r
6499 sel.cpMin = 999999;
\r
6500 sel.cpMax = 999999;
\r
6501 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6507 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6512 switch (message) {
\r
6514 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6517 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6520 sel.cpMin = 999999;
\r
6521 sel.cpMax = 999999;
\r
6522 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6523 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6528 if(wParam != '\022') {
\r
6529 if (wParam == '\t') {
\r
6530 if (GetKeyState(VK_SHIFT) < 0) {
\r
6532 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6533 if (buttonDesc[0].hwnd) {
\r
6534 SetFocus(buttonDesc[0].hwnd);
\r
6536 SetFocus(hwndMain);
\r
6540 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6543 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6544 JAWS_DELETE( SetFocus(hInput); )
\r
6545 SendMessage(hInput, message, wParam, lParam);
\r
6548 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6549 case WM_RBUTTONDOWN:
\r
6550 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6551 /* Move selection here if it was empty */
\r
6553 pt.x = LOWORD(lParam);
\r
6554 pt.y = HIWORD(lParam);
\r
6555 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6556 if (sel.cpMin == sel.cpMax) {
\r
6557 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6558 sel.cpMax = sel.cpMin;
\r
6559 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6561 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6562 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6564 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6565 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6566 if (sel.cpMin == sel.cpMax) {
\r
6567 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6568 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6570 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6571 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6573 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6574 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6575 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6576 MenuPopup(hwnd, pt, hmenu, -1);
\r
6580 case WM_RBUTTONUP:
\r
6581 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6582 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6583 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6587 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6589 return SendMessage(hInput, message, wParam, lParam);
\r
6590 case WM_MBUTTONDOWN:
\r
6591 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6593 switch (LOWORD(wParam)) {
\r
6594 case IDM_QuickPaste:
\r
6596 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6597 if (sel.cpMin == sel.cpMax) {
\r
6598 MessageBeep(MB_ICONEXCLAMATION);
\r
6601 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6602 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6603 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6608 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6611 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6614 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6618 int i = LOWORD(wParam) - IDM_CommandX;
\r
6619 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6620 icsTextMenuEntry[i].command != NULL) {
\r
6621 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6622 icsTextMenuEntry[i].getname,
\r
6623 icsTextMenuEntry[i].immediate);
\r
6631 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6634 WNDPROC consoleInputWindowProc;
\r
6637 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6639 char buf[MSG_SIZ];
\r
6641 static BOOL sendNextChar = FALSE;
\r
6642 static BOOL quoteNextChar = FALSE;
\r
6643 InputSource *is = consoleInputSource;
\r
6647 switch (message) {
\r
6649 if (!appData.localLineEditing || sendNextChar) {
\r
6650 is->buf[0] = (CHAR) wParam;
\r
6652 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6653 sendNextChar = FALSE;
\r
6656 if (quoteNextChar) {
\r
6657 buf[0] = (char) wParam;
\r
6658 buf[1] = NULLCHAR;
\r
6659 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6660 quoteNextChar = FALSE;
\r
6664 case '\r': /* Enter key */
\r
6665 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6666 if (consoleEcho) SaveInHistory(is->buf);
\r
6667 is->buf[is->count++] = '\n';
\r
6668 is->buf[is->count] = NULLCHAR;
\r
6669 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6670 if (consoleEcho) {
\r
6671 ConsoleOutput(is->buf, is->count, TRUE);
\r
6672 } else if (appData.localLineEditing) {
\r
6673 ConsoleOutput("\n", 1, TRUE);
\r
6676 case '\033': /* Escape key */
\r
6677 SetWindowText(hwnd, "");
\r
6678 cf.cbSize = sizeof(CHARFORMAT);
\r
6679 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6680 if (consoleEcho) {
\r
6681 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6683 cf.crTextColor = COLOR_ECHOOFF;
\r
6685 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6686 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6688 case '\t': /* Tab key */
\r
6689 if (GetKeyState(VK_SHIFT) < 0) {
\r
6691 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6694 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6695 if (buttonDesc[0].hwnd) {
\r
6696 SetFocus(buttonDesc[0].hwnd);
\r
6698 SetFocus(hwndMain);
\r
6702 case '\023': /* Ctrl+S */
\r
6703 sendNextChar = TRUE;
\r
6705 case '\021': /* Ctrl+Q */
\r
6706 quoteNextChar = TRUE;
\r
6716 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6717 p = PrevInHistory(buf);
\r
6719 SetWindowText(hwnd, p);
\r
6720 sel.cpMin = 999999;
\r
6721 sel.cpMax = 999999;
\r
6722 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6727 p = NextInHistory();
\r
6729 SetWindowText(hwnd, p);
\r
6730 sel.cpMin = 999999;
\r
6731 sel.cpMax = 999999;
\r
6732 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6738 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6742 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6746 case WM_MBUTTONDOWN:
\r
6747 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6748 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6750 case WM_RBUTTONUP:
\r
6751 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6752 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6753 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6757 hmenu = LoadMenu(hInst, "InputMenu");
\r
6758 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6759 if (sel.cpMin == sel.cpMax) {
\r
6760 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6761 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6763 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6764 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6766 pt.x = LOWORD(lParam);
\r
6767 pt.y = HIWORD(lParam);
\r
6768 MenuPopup(hwnd, pt, hmenu, -1);
\r
6772 switch (LOWORD(wParam)) {
\r
6774 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6776 case IDM_SelectAll:
\r
6778 sel.cpMax = -1; /*999999?*/
\r
6779 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6782 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6785 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6788 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6793 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6796 #define CO_MAX 100000
\r
6797 #define CO_TRIM 1000
\r
6800 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6802 static SnapData sd;
\r
6803 HWND hText, hInput;
\r
6805 static int sizeX, sizeY;
\r
6806 int newSizeX, newSizeY;
\r
6810 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6811 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6813 switch (message) {
\r
6815 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6817 ENLINK *pLink = (ENLINK*)lParam;
\r
6818 if (pLink->msg == WM_LBUTTONUP)
\r
6822 tr.chrg = pLink->chrg;
\r
6823 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6824 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6825 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6826 free(tr.lpstrText);
\r
6830 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6831 hwndConsole = hDlg;
\r
6833 consoleTextWindowProc = (WNDPROC)
\r
6834 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6835 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6836 consoleInputWindowProc = (WNDPROC)
\r
6837 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6838 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6839 Colorize(ColorNormal, TRUE);
\r
6840 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6841 ChangedConsoleFont();
\r
6842 GetClientRect(hDlg, &rect);
\r
6843 sizeX = rect.right;
\r
6844 sizeY = rect.bottom;
\r
6845 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6846 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6847 WINDOWPLACEMENT wp;
\r
6848 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6849 wp.length = sizeof(WINDOWPLACEMENT);
\r
6851 wp.showCmd = SW_SHOW;
\r
6852 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6853 wp.rcNormalPosition.left = wpConsole.x;
\r
6854 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6855 wp.rcNormalPosition.top = wpConsole.y;
\r
6856 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6857 SetWindowPlacement(hDlg, &wp);
\r
6860 // [HGM] Chessknight's change 2004-07-13
\r
6861 else { /* Determine Defaults */
\r
6862 WINDOWPLACEMENT wp;
\r
6863 wpConsole.x = wpMain.width + 1;
\r
6864 wpConsole.y = wpMain.y;
\r
6865 wpConsole.width = screenWidth - wpMain.width;
\r
6866 wpConsole.height = wpMain.height;
\r
6867 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6868 wp.length = sizeof(WINDOWPLACEMENT);
\r
6870 wp.showCmd = SW_SHOW;
\r
6871 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6872 wp.rcNormalPosition.left = wpConsole.x;
\r
6873 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6874 wp.rcNormalPosition.top = wpConsole.y;
\r
6875 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6876 SetWindowPlacement(hDlg, &wp);
\r
6879 // Allow hText to highlight URLs and send notifications on them
\r
6880 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6881 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6882 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6883 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6897 if (IsIconic(hDlg)) break;
\r
6898 newSizeX = LOWORD(lParam);
\r
6899 newSizeY = HIWORD(lParam);
\r
6900 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6901 RECT rectText, rectInput;
\r
6903 int newTextHeight, newTextWidth;
\r
6904 GetWindowRect(hText, &rectText);
\r
6905 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6906 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6907 if (newTextHeight < 0) {
\r
6908 newSizeY += -newTextHeight;
\r
6909 newTextHeight = 0;
\r
6911 SetWindowPos(hText, NULL, 0, 0,
\r
6912 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6913 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6914 pt.x = rectInput.left;
\r
6915 pt.y = rectInput.top + newSizeY - sizeY;
\r
6916 ScreenToClient(hDlg, &pt);
\r
6917 SetWindowPos(hInput, NULL,
\r
6918 pt.x, pt.y, /* needs client coords */
\r
6919 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6920 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6926 case WM_GETMINMAXINFO:
\r
6927 /* Prevent resizing window too small */
\r
6928 mmi = (MINMAXINFO *) lParam;
\r
6929 mmi->ptMinTrackSize.x = 100;
\r
6930 mmi->ptMinTrackSize.y = 100;
\r
6933 /* [AS] Snapping */
\r
6934 case WM_ENTERSIZEMOVE:
\r
6935 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6938 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6941 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6943 case WM_EXITSIZEMOVE:
\r
6944 UpdateICSWidth(hText);
\r
6945 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6948 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6956 if (hwndConsole) return;
\r
6957 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6958 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6963 ConsoleOutput(char* data, int length, int forceVisible)
\r
6968 char buf[CO_MAX+1];
\r
6971 static int delayLF = 0;
\r
6972 CHARRANGE savesel, sel;
\r
6974 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6982 while (length--) {
\r
6990 } else if (*p == '\007') {
\r
6991 MyPlaySound(&sounds[(int)SoundBell]);
\r
6998 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6999 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7000 /* Save current selection */
\r
7001 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7002 exlen = GetWindowTextLength(hText);
\r
7003 /* Find out whether current end of text is visible */
\r
7004 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7005 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7006 /* Trim existing text if it's too long */
\r
7007 if (exlen + (q - buf) > CO_MAX) {
\r
7008 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7011 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7012 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7014 savesel.cpMin -= trim;
\r
7015 savesel.cpMax -= trim;
\r
7016 if (exlen < 0) exlen = 0;
\r
7017 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7018 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7020 /* Append the new text */
\r
7021 sel.cpMin = exlen;
\r
7022 sel.cpMax = exlen;
\r
7023 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7024 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7025 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7026 if (forceVisible || exlen == 0 ||
\r
7027 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7028 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7029 /* Scroll to make new end of text visible if old end of text
\r
7030 was visible or new text is an echo of user typein */
\r
7031 sel.cpMin = 9999999;
\r
7032 sel.cpMax = 9999999;
\r
7033 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7034 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7035 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7036 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7038 if (savesel.cpMax == exlen || forceVisible) {
\r
7039 /* Move insert point to new end of text if it was at the old
\r
7040 end of text or if the new text is an echo of user typein */
\r
7041 sel.cpMin = 9999999;
\r
7042 sel.cpMax = 9999999;
\r
7043 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7045 /* Restore previous selection */
\r
7046 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7048 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7055 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7059 COLORREF oldFg, oldBg;
\r
7063 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7065 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7066 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7067 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7070 rect.right = x + squareSize;
\r
7072 rect.bottom = y + squareSize;
\r
7075 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7076 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7077 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7078 &rect, str, strlen(str), NULL);
\r
7080 (void) SetTextColor(hdc, oldFg);
\r
7081 (void) SetBkColor(hdc, oldBg);
\r
7082 (void) SelectObject(hdc, oldFont);
\r
7086 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7087 RECT *rect, char *color, char *flagFell)
\r
7091 COLORREF oldFg, oldBg;
\r
7094 if (appData.clockMode) {
\r
7096 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7098 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7105 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7106 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7108 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7109 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7111 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7115 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7116 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7117 rect, str, strlen(str), NULL);
\r
7118 if(logoHeight > 0 && appData.clockMode) {
\r
7120 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7121 r.top = rect->top + logoHeight/2;
\r
7122 r.left = rect->left;
\r
7123 r.right = rect->right;
\r
7124 r.bottom = rect->bottom;
\r
7125 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7126 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7127 &r, str, strlen(str), NULL);
\r
7129 (void) SetTextColor(hdc, oldFg);
\r
7130 (void) SetBkColor(hdc, oldBg);
\r
7131 (void) SelectObject(hdc, oldFont);
\r
7136 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7142 if( count <= 0 ) {
\r
7143 if (appData.debugMode) {
\r
7144 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7147 return ERROR_INVALID_USER_BUFFER;
\r
7150 ResetEvent(ovl->hEvent);
\r
7151 ovl->Offset = ovl->OffsetHigh = 0;
\r
7152 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7156 err = GetLastError();
\r
7157 if (err == ERROR_IO_PENDING) {
\r
7158 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7162 err = GetLastError();
\r
7169 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7174 ResetEvent(ovl->hEvent);
\r
7175 ovl->Offset = ovl->OffsetHigh = 0;
\r
7176 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7180 err = GetLastError();
\r
7181 if (err == ERROR_IO_PENDING) {
\r
7182 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7186 err = GetLastError();
\r
7192 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7193 void CheckForInputBufferFull( InputSource * is )
\r
7195 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7196 /* Look for end of line */
\r
7197 char * p = is->buf;
\r
7199 while( p < is->next && *p != '\n' ) {
\r
7203 if( p >= is->next ) {
\r
7204 if (appData.debugMode) {
\r
7205 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7208 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7209 is->count = (DWORD) -1;
\r
7210 is->next = is->buf;
\r
7216 InputThread(LPVOID arg)
\r
7221 is = (InputSource *) arg;
\r
7222 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7223 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7224 while (is->hThread != NULL) {
\r
7225 is->error = DoReadFile(is->hFile, is->next,
\r
7226 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7227 &is->count, &ovl);
\r
7228 if (is->error == NO_ERROR) {
\r
7229 is->next += is->count;
\r
7231 if (is->error == ERROR_BROKEN_PIPE) {
\r
7232 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7235 is->count = (DWORD) -1;
\r
7236 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7241 CheckForInputBufferFull( is );
\r
7243 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7245 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7247 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7250 CloseHandle(ovl.hEvent);
\r
7251 CloseHandle(is->hFile);
\r
7253 if (appData.debugMode) {
\r
7254 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7261 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7263 NonOvlInputThread(LPVOID arg)
\r
7270 is = (InputSource *) arg;
\r
7271 while (is->hThread != NULL) {
\r
7272 is->error = ReadFile(is->hFile, is->next,
\r
7273 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7274 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7275 if (is->error == NO_ERROR) {
\r
7276 /* Change CRLF to LF */
\r
7277 if (is->next > is->buf) {
\r
7279 i = is->count + 1;
\r
7287 if (prev == '\r' && *p == '\n') {
\r
7299 if (is->error == ERROR_BROKEN_PIPE) {
\r
7300 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7303 is->count = (DWORD) -1;
\r
7307 CheckForInputBufferFull( is );
\r
7309 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7311 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7313 if (is->count < 0) break; /* Quit on error */
\r
7315 CloseHandle(is->hFile);
\r
7320 SocketInputThread(LPVOID arg)
\r
7324 is = (InputSource *) arg;
\r
7325 while (is->hThread != NULL) {
\r
7326 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7327 if ((int)is->count == SOCKET_ERROR) {
\r
7328 is->count = (DWORD) -1;
\r
7329 is->error = WSAGetLastError();
\r
7331 is->error = NO_ERROR;
\r
7332 is->next += is->count;
\r
7333 if (is->count == 0 && is->second == is) {
\r
7334 /* End of file on stderr; quit with no message */
\r
7338 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7340 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7342 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7348 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7352 is = (InputSource *) lParam;
\r
7353 if (is->lineByLine) {
\r
7354 /* Feed in lines one by one */
\r
7355 char *p = is->buf;
\r
7357 while (q < is->next) {
\r
7358 if (*q++ == '\n') {
\r
7359 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7364 /* Move any partial line to the start of the buffer */
\r
7366 while (p < is->next) {
\r
7371 if (is->error != NO_ERROR || is->count == 0) {
\r
7372 /* Notify backend of the error. Note: If there was a partial
\r
7373 line at the end, it is not flushed through. */
\r
7374 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7377 /* Feed in the whole chunk of input at once */
\r
7378 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7379 is->next = is->buf;
\r
7383 /*---------------------------------------------------------------------------*\
\r
7385 * Menu enables. Used when setting various modes.
\r
7387 \*---------------------------------------------------------------------------*/
\r
7395 GreyRevert(Boolean grey)
\r
7396 { // [HGM] vari: for retracting variations in local mode
\r
7397 HMENU hmenu = GetMenu(hwndMain);
\r
7398 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7399 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7403 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7405 while (enab->item > 0) {
\r
7406 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7411 Enables gnuEnables[] = {
\r
7412 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7413 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7415 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7416 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7417 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7418 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7419 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7420 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7421 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7422 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7424 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7428 Enables icsEnables[] = {
\r
7429 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7436 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7437 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7439 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7440 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7441 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7442 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7443 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7444 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7449 Enables zippyEnables[] = {
\r
7450 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7451 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7452 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7453 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7458 Enables ncpEnables[] = {
\r
7459 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7460 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7461 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7463 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7464 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7465 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7466 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7467 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7468 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7471 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7472 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7473 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7474 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7475 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7476 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7477 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7482 Enables trainingOnEnables[] = {
\r
7483 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7484 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7485 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7486 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7487 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7488 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7489 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7490 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7494 Enables trainingOffEnables[] = {
\r
7495 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7496 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7497 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7498 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7499 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7500 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7501 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7502 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7506 /* These modify either ncpEnables or gnuEnables */
\r
7507 Enables cmailEnables[] = {
\r
7508 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7509 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7510 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7511 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7512 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7513 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7514 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7518 Enables machineThinkingEnables[] = {
\r
7519 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7520 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7521 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7522 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7523 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7524 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7525 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7526 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7527 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7528 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7529 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7530 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7531 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7532 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7533 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7537 Enables userThinkingEnables[] = {
\r
7538 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7539 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7540 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7541 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7542 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7543 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7544 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7545 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7546 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7547 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7548 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7549 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7550 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7551 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7552 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7556 /*---------------------------------------------------------------------------*\
\r
7558 * Front-end interface functions exported by XBoard.
\r
7559 * Functions appear in same order as prototypes in frontend.h.
\r
7561 \*---------------------------------------------------------------------------*/
\r
7565 static UINT prevChecked = 0;
\r
7566 static int prevPausing = 0;
\r
7569 if (pausing != prevPausing) {
\r
7570 prevPausing = pausing;
\r
7571 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7572 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7573 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7576 switch (gameMode) {
\r
7577 case BeginningOfGame:
\r
7578 if (appData.icsActive)
\r
7579 nowChecked = IDM_IcsClient;
\r
7580 else if (appData.noChessProgram)
\r
7581 nowChecked = IDM_EditGame;
\r
7583 nowChecked = IDM_MachineBlack;
\r
7585 case MachinePlaysBlack:
\r
7586 nowChecked = IDM_MachineBlack;
\r
7588 case MachinePlaysWhite:
\r
7589 nowChecked = IDM_MachineWhite;
\r
7591 case TwoMachinesPlay:
\r
7592 nowChecked = IDM_TwoMachines;
\r
7595 nowChecked = IDM_AnalysisMode;
\r
7598 nowChecked = IDM_AnalyzeFile;
\r
7601 nowChecked = IDM_EditGame;
\r
7603 case PlayFromGameFile:
\r
7604 nowChecked = IDM_LoadGame;
\r
7606 case EditPosition:
\r
7607 nowChecked = IDM_EditPosition;
\r
7610 nowChecked = IDM_Training;
\r
7612 case IcsPlayingWhite:
\r
7613 case IcsPlayingBlack:
\r
7614 case IcsObserving:
\r
7616 nowChecked = IDM_IcsClient;
\r
7623 if (prevChecked != 0)
\r
7624 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7625 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7626 if (nowChecked != 0)
\r
7627 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7628 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7630 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7631 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7632 MF_BYCOMMAND|MF_ENABLED);
\r
7634 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7635 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7638 prevChecked = nowChecked;
\r
7640 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7641 if (appData.icsActive) {
\r
7642 if (appData.icsEngineAnalyze) {
\r
7643 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7644 MF_BYCOMMAND|MF_CHECKED);
\r
7646 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7647 MF_BYCOMMAND|MF_UNCHECKED);
\r
7655 HMENU hmenu = GetMenu(hwndMain);
\r
7656 SetMenuEnables(hmenu, icsEnables);
\r
7657 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7658 MF_BYPOSITION|MF_ENABLED);
\r
7660 if (appData.zippyPlay) {
\r
7661 SetMenuEnables(hmenu, zippyEnables);
\r
7662 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7663 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7664 MF_BYCOMMAND|MF_ENABLED);
\r
7672 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7678 HMENU hmenu = GetMenu(hwndMain);
\r
7679 SetMenuEnables(hmenu, ncpEnables);
\r
7680 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7681 MF_BYPOSITION|MF_GRAYED);
\r
7682 DrawMenuBar(hwndMain);
\r
7688 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7692 SetTrainingModeOn()
\r
7695 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7696 for (i = 0; i < N_BUTTONS; i++) {
\r
7697 if (buttonDesc[i].hwnd != NULL)
\r
7698 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7703 VOID SetTrainingModeOff()
\r
7706 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7707 for (i = 0; i < N_BUTTONS; i++) {
\r
7708 if (buttonDesc[i].hwnd != NULL)
\r
7709 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7715 SetUserThinkingEnables()
\r
7717 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7721 SetMachineThinkingEnables()
\r
7723 HMENU hMenu = GetMenu(hwndMain);
\r
7724 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7726 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7728 if (gameMode == MachinePlaysBlack) {
\r
7729 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7730 } else if (gameMode == MachinePlaysWhite) {
\r
7731 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7732 } else if (gameMode == TwoMachinesPlay) {
\r
7733 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7739 DisplayTitle(char *str)
\r
7741 char title[MSG_SIZ], *host;
\r
7742 if (str[0] != NULLCHAR) {
\r
7743 strcpy(title, str);
\r
7744 } else if (appData.icsActive) {
\r
7745 if (appData.icsCommPort[0] != NULLCHAR)
\r
7748 host = appData.icsHost;
\r
7749 sprintf(title, "%s: %s", szTitle, host);
\r
7750 } else if (appData.noChessProgram) {
\r
7751 strcpy(title, szTitle);
\r
7753 strcpy(title, szTitle);
\r
7754 strcat(title, ": ");
\r
7755 strcat(title, first.tidy);
\r
7757 SetWindowText(hwndMain, title);
\r
7762 DisplayMessage(char *str1, char *str2)
\r
7766 int remain = MESSAGE_TEXT_MAX - 1;
\r
7769 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7770 messageText[0] = NULLCHAR;
\r
7772 len = strlen(str1);
\r
7773 if (len > remain) len = remain;
\r
7774 strncpy(messageText, str1, len);
\r
7775 messageText[len] = NULLCHAR;
\r
7778 if (*str2 && remain >= 2) {
\r
7780 strcat(messageText, " ");
\r
7783 len = strlen(str2);
\r
7784 if (len > remain) len = remain;
\r
7785 strncat(messageText, str2, len);
\r
7787 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7789 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7793 hdc = GetDC(hwndMain);
\r
7794 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7795 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7796 &messageRect, messageText, strlen(messageText), NULL);
\r
7797 (void) SelectObject(hdc, oldFont);
\r
7798 (void) ReleaseDC(hwndMain, hdc);
\r
7802 DisplayError(char *str, int error)
\r
7804 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7810 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7811 NULL, error, LANG_NEUTRAL,
\r
7812 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7814 sprintf(buf, "%s:\n%s", str, buf2);
\r
7816 ErrorMap *em = errmap;
\r
7817 while (em->err != 0 && em->err != error) em++;
\r
7818 if (em->err != 0) {
\r
7819 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7821 sprintf(buf, "%s:\nError code %d", str, error);
\r
7826 ErrorPopUp("Error", buf);
\r
7831 DisplayMoveError(char *str)
\r
7833 fromX = fromY = -1;
\r
7834 ClearHighlights();
\r
7835 DrawPosition(FALSE, NULL);
\r
7836 if (appData.popupMoveErrors) {
\r
7837 ErrorPopUp("Error", str);
\r
7839 DisplayMessage(str, "");
\r
7840 moveErrorMessageUp = TRUE;
\r
7845 DisplayFatalError(char *str, int error, int exitStatus)
\r
7847 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7849 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7852 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7853 NULL, error, LANG_NEUTRAL,
\r
7854 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7856 sprintf(buf, "%s:\n%s", str, buf2);
\r
7858 ErrorMap *em = errmap;
\r
7859 while (em->err != 0 && em->err != error) em++;
\r
7860 if (em->err != 0) {
\r
7861 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7863 sprintf(buf, "%s:\nError code %d", str, error);
\r
7868 if (appData.debugMode) {
\r
7869 fprintf(debugFP, "%s: %s\n", label, str);
\r
7871 if (appData.popupExitMessage) {
\r
7872 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7873 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7875 ExitEvent(exitStatus);
\r
7880 DisplayInformation(char *str)
\r
7882 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7887 DisplayNote(char *str)
\r
7889 ErrorPopUp("Note", str);
\r
7894 char *title, *question, *replyPrefix;
\r
7899 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7901 static QuestionParams *qp;
\r
7902 char reply[MSG_SIZ];
\r
7905 switch (message) {
\r
7906 case WM_INITDIALOG:
\r
7907 qp = (QuestionParams *) lParam;
\r
7908 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7909 SetWindowText(hDlg, qp->title);
\r
7910 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7911 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7915 switch (LOWORD(wParam)) {
\r
7917 strcpy(reply, qp->replyPrefix);
\r
7918 if (*reply) strcat(reply, " ");
\r
7919 len = strlen(reply);
\r
7920 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7921 strcat(reply, "\n");
\r
7922 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7923 EndDialog(hDlg, TRUE);
\r
7924 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7927 EndDialog(hDlg, FALSE);
\r
7938 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7940 QuestionParams qp;
\r
7944 qp.question = question;
\r
7945 qp.replyPrefix = replyPrefix;
\r
7947 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7948 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7949 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7950 FreeProcInstance(lpProc);
\r
7953 /* [AS] Pick FRC position */
\r
7954 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7956 static int * lpIndexFRC;
\r
7962 case WM_INITDIALOG:
\r
7963 lpIndexFRC = (int *) lParam;
\r
7965 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7967 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7968 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7969 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7970 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7975 switch( LOWORD(wParam) ) {
\r
7977 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7978 EndDialog( hDlg, 0 );
\r
7979 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7982 EndDialog( hDlg, 1 );
\r
7984 case IDC_NFG_Edit:
\r
7985 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7986 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7988 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7991 case IDC_NFG_Random:
\r
7992 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7993 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8006 int index = appData.defaultFrcPosition;
\r
8007 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8009 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8011 if( result == 0 ) {
\r
8012 appData.defaultFrcPosition = index;
\r
8018 /* [AS] Game list options. Refactored by HGM */
\r
8020 HWND gameListOptionsDialog;
\r
8022 // low-level front-end: clear text edit / list widget
\r
8026 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8029 // low-level front-end: clear text edit / list widget
\r
8031 GLT_DeSelectList()
\r
8033 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8036 // low-level front-end: append line to text edit / list widget
\r
8038 GLT_AddToList( char *name )
\r
8041 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8045 // low-level front-end: get line from text edit / list widget
\r
8047 GLT_GetFromList( int index, char *name )
\r
8050 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8056 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8058 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8059 int idx2 = idx1 + delta;
\r
8060 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8062 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8065 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8066 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8067 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8068 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8072 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8076 case WM_INITDIALOG:
\r
8077 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8079 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8081 /* Initialize list */
\r
8082 GLT_TagsToList( lpUserGLT );
\r
8084 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8089 switch( LOWORD(wParam) ) {
\r
8092 EndDialog( hDlg, 0 );
\r
8095 EndDialog( hDlg, 1 );
\r
8098 case IDC_GLT_Default:
\r
8099 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8102 case IDC_GLT_Restore:
\r
8103 GLT_TagsToList( appData.gameListTags );
\r
8107 GLT_MoveSelection( hDlg, -1 );
\r
8110 case IDC_GLT_Down:
\r
8111 GLT_MoveSelection( hDlg, +1 );
\r
8121 int GameListOptions()
\r
8124 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8126 strcpy( lpUserGLT, appData.gameListTags );
\r
8128 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8130 if( result == 0 ) {
\r
8131 /* [AS] Memory leak here! */
\r
8132 appData.gameListTags = strdup( lpUserGLT );
\r
8139 DisplayIcsInteractionTitle(char *str)
\r
8141 char consoleTitle[MSG_SIZ];
\r
8143 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8144 SetWindowText(hwndConsole, consoleTitle);
\r
8148 DrawPosition(int fullRedraw, Board board)
\r
8150 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8153 void NotifyFrontendLogin()
\r
8156 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8162 fromX = fromY = -1;
\r
8163 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8164 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8165 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8166 dragInfo.lastpos = dragInfo.pos;
\r
8167 dragInfo.start.x = dragInfo.start.y = -1;
\r
8168 dragInfo.from = dragInfo.start;
\r
8170 DrawPosition(TRUE, NULL);
\r
8176 CommentPopUp(char *title, char *str)
\r
8178 HWND hwnd = GetActiveWindow();
\r
8179 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8181 SetActiveWindow(hwnd);
\r
8185 CommentPopDown(void)
\r
8187 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8188 if (commentDialog) {
\r
8189 ShowWindow(commentDialog, SW_HIDE);
\r
8191 commentUp = FALSE;
\r
8195 EditCommentPopUp(int index, char *title, char *str)
\r
8197 EitherCommentPopUp(index, title, str, TRUE);
\r
8204 MyPlaySound(&sounds[(int)SoundMove]);
\r
8207 VOID PlayIcsWinSound()
\r
8209 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8212 VOID PlayIcsLossSound()
\r
8214 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8217 VOID PlayIcsDrawSound()
\r
8219 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8222 VOID PlayIcsUnfinishedSound()
\r
8224 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8230 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8238 consoleEcho = TRUE;
\r
8239 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8240 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8241 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8250 consoleEcho = FALSE;
\r
8251 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8252 /* This works OK: set text and background both to the same color */
\r
8254 cf.crTextColor = COLOR_ECHOOFF;
\r
8255 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8256 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8259 /* No Raw()...? */
\r
8261 void Colorize(ColorClass cc, int continuation)
\r
8263 currentColorClass = cc;
\r
8264 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8265 consoleCF.crTextColor = textAttribs[cc].color;
\r
8266 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8267 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8273 static char buf[MSG_SIZ];
\r
8274 DWORD bufsiz = MSG_SIZ;
\r
8276 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8277 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8279 if (!GetUserName(buf, &bufsiz)) {
\r
8280 /*DisplayError("Error getting user name", GetLastError());*/
\r
8281 strcpy(buf, "User");
\r
8289 static char buf[MSG_SIZ];
\r
8290 DWORD bufsiz = MSG_SIZ;
\r
8292 if (!GetComputerName(buf, &bufsiz)) {
\r
8293 /*DisplayError("Error getting host name", GetLastError());*/
\r
8294 strcpy(buf, "Unknown");
\r
8301 ClockTimerRunning()
\r
8303 return clockTimerEvent != 0;
\r
8309 if (clockTimerEvent == 0) return FALSE;
\r
8310 KillTimer(hwndMain, clockTimerEvent);
\r
8311 clockTimerEvent = 0;
\r
8316 StartClockTimer(long millisec)
\r
8318 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8319 (UINT) millisec, NULL);
\r
8323 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8326 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8328 if(appData.noGUI) return;
\r
8329 hdc = GetDC(hwndMain);
\r
8330 if (!IsIconic(hwndMain)) {
\r
8331 DisplayAClock(hdc, timeRemaining, highlight,
\r
8332 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8334 if (highlight && iconCurrent == iconBlack) {
\r
8335 iconCurrent = iconWhite;
\r
8336 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8337 if (IsIconic(hwndMain)) {
\r
8338 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8341 (void) ReleaseDC(hwndMain, hdc);
\r
8343 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8347 DisplayBlackClock(long timeRemaining, int highlight)
\r
8350 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8352 if(appData.noGUI) return;
\r
8353 hdc = GetDC(hwndMain);
\r
8354 if (!IsIconic(hwndMain)) {
\r
8355 DisplayAClock(hdc, timeRemaining, highlight,
\r
8356 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8358 if (highlight && iconCurrent == iconWhite) {
\r
8359 iconCurrent = iconBlack;
\r
8360 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8361 if (IsIconic(hwndMain)) {
\r
8362 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8365 (void) ReleaseDC(hwndMain, hdc);
\r
8367 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8372 LoadGameTimerRunning()
\r
8374 return loadGameTimerEvent != 0;
\r
8378 StopLoadGameTimer()
\r
8380 if (loadGameTimerEvent == 0) return FALSE;
\r
8381 KillTimer(hwndMain, loadGameTimerEvent);
\r
8382 loadGameTimerEvent = 0;
\r
8387 StartLoadGameTimer(long millisec)
\r
8389 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8390 (UINT) millisec, NULL);
\r
8398 char fileTitle[MSG_SIZ];
\r
8400 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8401 f = OpenFileDialog(hwndMain, "a", defName,
\r
8402 appData.oldSaveStyle ? "gam" : "pgn",
\r
8404 "Save Game to File", NULL, fileTitle, NULL);
\r
8406 SaveGame(f, 0, "");
\r
8413 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8415 if (delayedTimerEvent != 0) {
\r
8416 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8417 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8419 KillTimer(hwndMain, delayedTimerEvent);
\r
8420 delayedTimerEvent = 0;
\r
8421 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8422 delayedTimerCallback();
\r
8424 delayedTimerCallback = cb;
\r
8425 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8426 (UINT) millisec, NULL);
\r
8429 DelayedEventCallback
\r
8432 if (delayedTimerEvent) {
\r
8433 return delayedTimerCallback;
\r
8440 CancelDelayedEvent()
\r
8442 if (delayedTimerEvent) {
\r
8443 KillTimer(hwndMain, delayedTimerEvent);
\r
8444 delayedTimerEvent = 0;
\r
8448 DWORD GetWin32Priority(int nice)
\r
8449 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8451 REALTIME_PRIORITY_CLASS 0x00000100
\r
8452 HIGH_PRIORITY_CLASS 0x00000080
\r
8453 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8454 NORMAL_PRIORITY_CLASS 0x00000020
\r
8455 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8456 IDLE_PRIORITY_CLASS 0x00000040
\r
8458 if (nice < -15) return 0x00000080;
\r
8459 if (nice < 0) return 0x00008000;
\r
8460 if (nice == 0) return 0x00000020;
\r
8461 if (nice < 15) return 0x00004000;
\r
8462 return 0x00000040;
\r
8465 /* Start a child process running the given program.
\r
8466 The process's standard output can be read from "from", and its
\r
8467 standard input can be written to "to".
\r
8468 Exit with fatal error if anything goes wrong.
\r
8469 Returns an opaque pointer that can be used to destroy the process
\r
8473 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8475 #define BUFSIZE 4096
\r
8477 HANDLE hChildStdinRd, hChildStdinWr,
\r
8478 hChildStdoutRd, hChildStdoutWr;
\r
8479 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8480 SECURITY_ATTRIBUTES saAttr;
\r
8482 PROCESS_INFORMATION piProcInfo;
\r
8483 STARTUPINFO siStartInfo;
\r
8485 char buf[MSG_SIZ];
\r
8488 if (appData.debugMode) {
\r
8489 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8494 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8495 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8496 saAttr.bInheritHandle = TRUE;
\r
8497 saAttr.lpSecurityDescriptor = NULL;
\r
8500 * The steps for redirecting child's STDOUT:
\r
8501 * 1. Create anonymous pipe to be STDOUT for child.
\r
8502 * 2. Create a noninheritable duplicate of read handle,
\r
8503 * and close the inheritable read handle.
\r
8506 /* Create a pipe for the child's STDOUT. */
\r
8507 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8508 return GetLastError();
\r
8511 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8512 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8513 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8514 FALSE, /* not inherited */
\r
8515 DUPLICATE_SAME_ACCESS);
\r
8517 return GetLastError();
\r
8519 CloseHandle(hChildStdoutRd);
\r
8522 * The steps for redirecting child's STDIN:
\r
8523 * 1. Create anonymous pipe to be STDIN for child.
\r
8524 * 2. Create a noninheritable duplicate of write handle,
\r
8525 * and close the inheritable write handle.
\r
8528 /* Create a pipe for the child's STDIN. */
\r
8529 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8530 return GetLastError();
\r
8533 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8534 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8535 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8536 FALSE, /* not inherited */
\r
8537 DUPLICATE_SAME_ACCESS);
\r
8539 return GetLastError();
\r
8541 CloseHandle(hChildStdinWr);
\r
8543 /* Arrange to (1) look in dir for the child .exe file, and
\r
8544 * (2) have dir be the child's working directory. Interpret
\r
8545 * dir relative to the directory WinBoard loaded from. */
\r
8546 GetCurrentDirectory(MSG_SIZ, buf);
\r
8547 SetCurrentDirectory(installDir);
\r
8548 SetCurrentDirectory(dir);
\r
8550 /* Now create the child process. */
\r
8552 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8553 siStartInfo.lpReserved = NULL;
\r
8554 siStartInfo.lpDesktop = NULL;
\r
8555 siStartInfo.lpTitle = NULL;
\r
8556 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8557 siStartInfo.cbReserved2 = 0;
\r
8558 siStartInfo.lpReserved2 = NULL;
\r
8559 siStartInfo.hStdInput = hChildStdinRd;
\r
8560 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8561 siStartInfo.hStdError = hChildStdoutWr;
\r
8563 fSuccess = CreateProcess(NULL,
\r
8564 cmdLine, /* command line */
\r
8565 NULL, /* process security attributes */
\r
8566 NULL, /* primary thread security attrs */
\r
8567 TRUE, /* handles are inherited */
\r
8568 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8569 NULL, /* use parent's environment */
\r
8571 &siStartInfo, /* STARTUPINFO pointer */
\r
8572 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8574 err = GetLastError();
\r
8575 SetCurrentDirectory(buf); /* return to prev directory */
\r
8580 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8581 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8582 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8585 /* Close the handles we don't need in the parent */
\r
8586 CloseHandle(piProcInfo.hThread);
\r
8587 CloseHandle(hChildStdinRd);
\r
8588 CloseHandle(hChildStdoutWr);
\r
8590 /* Prepare return value */
\r
8591 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8592 cp->kind = CPReal;
\r
8593 cp->hProcess = piProcInfo.hProcess;
\r
8594 cp->pid = piProcInfo.dwProcessId;
\r
8595 cp->hFrom = hChildStdoutRdDup;
\r
8596 cp->hTo = hChildStdinWrDup;
\r
8598 *pr = (void *) cp;
\r
8600 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8601 2000 where engines sometimes don't see the initial command(s)
\r
8602 from WinBoard and hang. I don't understand how that can happen,
\r
8603 but the Sleep is harmless, so I've put it in. Others have also
\r
8604 reported what may be the same problem, so hopefully this will fix
\r
8605 it for them too. */
\r
8613 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8615 ChildProc *cp; int result;
\r
8617 cp = (ChildProc *) pr;
\r
8618 if (cp == NULL) return;
\r
8620 switch (cp->kind) {
\r
8622 /* TerminateProcess is considered harmful, so... */
\r
8623 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8624 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8625 /* The following doesn't work because the chess program
\r
8626 doesn't "have the same console" as WinBoard. Maybe
\r
8627 we could arrange for this even though neither WinBoard
\r
8628 nor the chess program uses a console for stdio? */
\r
8629 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8631 /* [AS] Special termination modes for misbehaving programs... */
\r
8632 if( signal == 9 ) {
\r
8633 result = TerminateProcess( cp->hProcess, 0 );
\r
8635 if ( appData.debugMode) {
\r
8636 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8639 else if( signal == 10 ) {
\r
8640 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8642 if( dw != WAIT_OBJECT_0 ) {
\r
8643 result = TerminateProcess( cp->hProcess, 0 );
\r
8645 if ( appData.debugMode) {
\r
8646 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8652 CloseHandle(cp->hProcess);
\r
8656 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8660 closesocket(cp->sock);
\r
8665 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8666 closesocket(cp->sock);
\r
8667 closesocket(cp->sock2);
\r
8675 InterruptChildProcess(ProcRef pr)
\r
8679 cp = (ChildProc *) pr;
\r
8680 if (cp == NULL) return;
\r
8681 switch (cp->kind) {
\r
8683 /* The following doesn't work because the chess program
\r
8684 doesn't "have the same console" as WinBoard. Maybe
\r
8685 we could arrange for this even though neither WinBoard
\r
8686 nor the chess program uses a console for stdio */
\r
8687 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8692 /* Can't interrupt */
\r
8696 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8703 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8705 char cmdLine[MSG_SIZ];
\r
8707 if (port[0] == NULLCHAR) {
\r
8708 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8710 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8712 return StartChildProcess(cmdLine, "", pr);
\r
8716 /* Code to open TCP sockets */
\r
8719 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8724 struct sockaddr_in sa, mysa;
\r
8725 struct hostent FAR *hp;
\r
8726 unsigned short uport;
\r
8727 WORD wVersionRequested;
\r
8730 /* Initialize socket DLL */
\r
8731 wVersionRequested = MAKEWORD(1, 1);
\r
8732 err = WSAStartup(wVersionRequested, &wsaData);
\r
8733 if (err != 0) return err;
\r
8736 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8737 err = WSAGetLastError();
\r
8742 /* Bind local address using (mostly) don't-care values.
\r
8744 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8745 mysa.sin_family = AF_INET;
\r
8746 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8747 uport = (unsigned short) 0;
\r
8748 mysa.sin_port = htons(uport);
\r
8749 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8750 == SOCKET_ERROR) {
\r
8751 err = WSAGetLastError();
\r
8756 /* Resolve remote host name */
\r
8757 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8758 if (!(hp = gethostbyname(host))) {
\r
8759 unsigned int b0, b1, b2, b3;
\r
8761 err = WSAGetLastError();
\r
8763 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8764 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8765 hp->h_addrtype = AF_INET;
\r
8767 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8768 hp->h_addr_list[0] = (char *) malloc(4);
\r
8769 hp->h_addr_list[0][0] = (char) b0;
\r
8770 hp->h_addr_list[0][1] = (char) b1;
\r
8771 hp->h_addr_list[0][2] = (char) b2;
\r
8772 hp->h_addr_list[0][3] = (char) b3;
\r
8778 sa.sin_family = hp->h_addrtype;
\r
8779 uport = (unsigned short) atoi(port);
\r
8780 sa.sin_port = htons(uport);
\r
8781 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8783 /* Make connection */
\r
8784 if (connect(s, (struct sockaddr *) &sa,
\r
8785 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8786 err = WSAGetLastError();
\r
8791 /* Prepare return value */
\r
8792 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8793 cp->kind = CPSock;
\r
8795 *pr = (ProcRef *) cp;
\r
8801 OpenCommPort(char *name, ProcRef *pr)
\r
8806 char fullname[MSG_SIZ];
\r
8808 if (*name != '\\')
\r
8809 sprintf(fullname, "\\\\.\\%s", name);
\r
8811 strcpy(fullname, name);
\r
8813 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8814 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8815 if (h == (HANDLE) -1) {
\r
8816 return GetLastError();
\r
8820 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8822 /* Accumulate characters until a 100ms pause, then parse */
\r
8823 ct.ReadIntervalTimeout = 100;
\r
8824 ct.ReadTotalTimeoutMultiplier = 0;
\r
8825 ct.ReadTotalTimeoutConstant = 0;
\r
8826 ct.WriteTotalTimeoutMultiplier = 0;
\r
8827 ct.WriteTotalTimeoutConstant = 0;
\r
8828 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8830 /* Prepare return value */
\r
8831 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8832 cp->kind = CPComm;
\r
8835 *pr = (ProcRef *) cp;
\r
8841 OpenLoopback(ProcRef *pr)
\r
8843 DisplayFatalError("Not implemented", 0, 1);
\r
8849 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8854 struct sockaddr_in sa, mysa;
\r
8855 struct hostent FAR *hp;
\r
8856 unsigned short uport;
\r
8857 WORD wVersionRequested;
\r
8860 char stderrPortStr[MSG_SIZ];
\r
8862 /* Initialize socket DLL */
\r
8863 wVersionRequested = MAKEWORD(1, 1);
\r
8864 err = WSAStartup(wVersionRequested, &wsaData);
\r
8865 if (err != 0) return err;
\r
8867 /* Resolve remote host name */
\r
8868 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8869 if (!(hp = gethostbyname(host))) {
\r
8870 unsigned int b0, b1, b2, b3;
\r
8872 err = WSAGetLastError();
\r
8874 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8875 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8876 hp->h_addrtype = AF_INET;
\r
8878 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8879 hp->h_addr_list[0] = (char *) malloc(4);
\r
8880 hp->h_addr_list[0][0] = (char) b0;
\r
8881 hp->h_addr_list[0][1] = (char) b1;
\r
8882 hp->h_addr_list[0][2] = (char) b2;
\r
8883 hp->h_addr_list[0][3] = (char) b3;
\r
8889 sa.sin_family = hp->h_addrtype;
\r
8890 uport = (unsigned short) 514;
\r
8891 sa.sin_port = htons(uport);
\r
8892 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8894 /* Bind local socket to unused "privileged" port address
\r
8896 s = INVALID_SOCKET;
\r
8897 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8898 mysa.sin_family = AF_INET;
\r
8899 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8900 for (fromPort = 1023;; fromPort--) {
\r
8901 if (fromPort < 0) {
\r
8903 return WSAEADDRINUSE;
\r
8905 if (s == INVALID_SOCKET) {
\r
8906 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8907 err = WSAGetLastError();
\r
8912 uport = (unsigned short) fromPort;
\r
8913 mysa.sin_port = htons(uport);
\r
8914 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8915 == SOCKET_ERROR) {
\r
8916 err = WSAGetLastError();
\r
8917 if (err == WSAEADDRINUSE) continue;
\r
8921 if (connect(s, (struct sockaddr *) &sa,
\r
8922 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8923 err = WSAGetLastError();
\r
8924 if (err == WSAEADDRINUSE) {
\r
8935 /* Bind stderr local socket to unused "privileged" port address
\r
8937 s2 = INVALID_SOCKET;
\r
8938 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8939 mysa.sin_family = AF_INET;
\r
8940 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8941 for (fromPort = 1023;; fromPort--) {
\r
8942 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8943 if (fromPort < 0) {
\r
8944 (void) closesocket(s);
\r
8946 return WSAEADDRINUSE;
\r
8948 if (s2 == INVALID_SOCKET) {
\r
8949 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8950 err = WSAGetLastError();
\r
8956 uport = (unsigned short) fromPort;
\r
8957 mysa.sin_port = htons(uport);
\r
8958 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8959 == SOCKET_ERROR) {
\r
8960 err = WSAGetLastError();
\r
8961 if (err == WSAEADDRINUSE) continue;
\r
8962 (void) closesocket(s);
\r
8966 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8967 err = WSAGetLastError();
\r
8968 if (err == WSAEADDRINUSE) {
\r
8970 s2 = INVALID_SOCKET;
\r
8973 (void) closesocket(s);
\r
8974 (void) closesocket(s2);
\r
8980 prevStderrPort = fromPort; // remember port used
\r
8981 sprintf(stderrPortStr, "%d", fromPort);
\r
8983 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8984 err = WSAGetLastError();
\r
8985 (void) closesocket(s);
\r
8986 (void) closesocket(s2);
\r
8991 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8992 err = WSAGetLastError();
\r
8993 (void) closesocket(s);
\r
8994 (void) closesocket(s2);
\r
8998 if (*user == NULLCHAR) user = UserName();
\r
8999 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9000 err = WSAGetLastError();
\r
9001 (void) closesocket(s);
\r
9002 (void) closesocket(s2);
\r
9006 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9007 err = WSAGetLastError();
\r
9008 (void) closesocket(s);
\r
9009 (void) closesocket(s2);
\r
9014 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9015 err = WSAGetLastError();
\r
9016 (void) closesocket(s);
\r
9017 (void) closesocket(s2);
\r
9021 (void) closesocket(s2); /* Stop listening */
\r
9023 /* Prepare return value */
\r
9024 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9025 cp->kind = CPRcmd;
\r
9028 *pr = (ProcRef *) cp;
\r
9035 AddInputSource(ProcRef pr, int lineByLine,
\r
9036 InputCallback func, VOIDSTAR closure)
\r
9038 InputSource *is, *is2 = NULL;
\r
9039 ChildProc *cp = (ChildProc *) pr;
\r
9041 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9042 is->lineByLine = lineByLine;
\r
9044 is->closure = closure;
\r
9045 is->second = NULL;
\r
9046 is->next = is->buf;
\r
9047 if (pr == NoProc) {
\r
9048 is->kind = CPReal;
\r
9049 consoleInputSource = is;
\r
9051 is->kind = cp->kind;
\r
9053 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9054 we create all threads suspended so that the is->hThread variable can be
\r
9055 safely assigned, then let the threads start with ResumeThread.
\r
9057 switch (cp->kind) {
\r
9059 is->hFile = cp->hFrom;
\r
9060 cp->hFrom = NULL; /* now owned by InputThread */
\r
9062 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9063 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9067 is->hFile = cp->hFrom;
\r
9068 cp->hFrom = NULL; /* now owned by InputThread */
\r
9070 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9071 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9075 is->sock = cp->sock;
\r
9077 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9078 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9082 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9084 is->sock = cp->sock;
\r
9086 is2->sock = cp->sock2;
\r
9087 is2->second = is2;
\r
9089 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9090 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9092 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9093 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9097 if( is->hThread != NULL ) {
\r
9098 ResumeThread( is->hThread );
\r
9101 if( is2 != NULL && is2->hThread != NULL ) {
\r
9102 ResumeThread( is2->hThread );
\r
9106 return (InputSourceRef) is;
\r
9110 RemoveInputSource(InputSourceRef isr)
\r
9114 is = (InputSource *) isr;
\r
9115 is->hThread = NULL; /* tell thread to stop */
\r
9116 CloseHandle(is->hThread);
\r
9117 if (is->second != NULL) {
\r
9118 is->second->hThread = NULL;
\r
9119 CloseHandle(is->second->hThread);
\r
9123 int no_wrap(char *message, int count)
\r
9125 ConsoleOutput(message, count, FALSE);
\r
9130 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9133 int outCount = SOCKET_ERROR;
\r
9134 ChildProc *cp = (ChildProc *) pr;
\r
9135 static OVERLAPPED ovl;
\r
9136 static int line = 0;
\r
9140 if (appData.noJoin || !appData.useInternalWrap)
\r
9141 return no_wrap(message, count);
\r
9144 int width = get_term_width();
\r
9145 int len = wrap(NULL, message, count, width, &line);
\r
9146 char *msg = malloc(len);
\r
9150 return no_wrap(message, count);
\r
9153 dbgchk = wrap(msg, message, count, width, &line);
\r
9154 if (dbgchk != len && appData.debugMode)
\r
9155 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9156 ConsoleOutput(msg, len, FALSE);
\r
9163 if (ovl.hEvent == NULL) {
\r
9164 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9166 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9168 switch (cp->kind) {
\r
9171 outCount = send(cp->sock, message, count, 0);
\r
9172 if (outCount == SOCKET_ERROR) {
\r
9173 *outError = WSAGetLastError();
\r
9175 *outError = NO_ERROR;
\r
9180 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9181 &dOutCount, NULL)) {
\r
9182 *outError = NO_ERROR;
\r
9183 outCount = (int) dOutCount;
\r
9185 *outError = GetLastError();
\r
9190 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9191 &dOutCount, &ovl);
\r
9192 if (*outError == NO_ERROR) {
\r
9193 outCount = (int) dOutCount;
\r
9201 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9204 /* Ignore delay, not implemented for WinBoard */
\r
9205 return OutputToProcess(pr, message, count, outError);
\r
9210 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9211 char *buf, int count, int error)
\r
9213 DisplayFatalError("Not implemented", 0, 1);
\r
9216 /* see wgamelist.c for Game List functions */
\r
9217 /* see wedittags.c for Edit Tags functions */
\r
9224 char buf[MSG_SIZ];
\r
9227 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9228 f = fopen(buf, "r");
\r
9230 ProcessICSInitScript(f);
\r
9238 StartAnalysisClock()
\r
9240 if (analysisTimerEvent) return;
\r
9241 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9242 (UINT) 2000, NULL);
\r
9246 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9248 highlightInfo.sq[0].x = fromX;
\r
9249 highlightInfo.sq[0].y = fromY;
\r
9250 highlightInfo.sq[1].x = toX;
\r
9251 highlightInfo.sq[1].y = toY;
\r
9257 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9258 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9262 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9264 premoveHighlightInfo.sq[0].x = fromX;
\r
9265 premoveHighlightInfo.sq[0].y = fromY;
\r
9266 premoveHighlightInfo.sq[1].x = toX;
\r
9267 premoveHighlightInfo.sq[1].y = toY;
\r
9271 ClearPremoveHighlights()
\r
9273 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9274 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9278 ShutDownFrontEnd()
\r
9280 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9281 DeleteClipboardTempFiles();
\r
9287 if (IsIconic(hwndMain))
\r
9288 ShowWindow(hwndMain, SW_RESTORE);
\r
9290 SetActiveWindow(hwndMain);
\r
9294 * Prototypes for animation support routines
\r
9296 static void ScreenSquare(int column, int row, POINT * pt);
\r
9297 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9298 POINT frames[], int * nFrames);
\r
9302 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9303 { // [HGM] atomic: animate blast wave
\r
9305 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9306 explodeInfo.fromX = fromX;
\r
9307 explodeInfo.fromY = fromY;
\r
9308 explodeInfo.toX = toX;
\r
9309 explodeInfo.toY = toY;
\r
9310 for(i=1; i<nFrames; i++) {
\r
9311 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9312 DrawPosition(FALSE, NULL);
\r
9313 Sleep(appData.animSpeed);
\r
9315 explodeInfo.radius = 0;
\r
9316 DrawPosition(TRUE, NULL);
\r
9322 AnimateMove(board, fromX, fromY, toX, toY)
\r
9329 ChessSquare piece;
\r
9330 POINT start, finish, mid;
\r
9331 POINT frames[kFactor * 2 + 1];
\r
9334 if (!appData.animate) return;
\r
9335 if (doingSizing) return;
\r
9336 if (fromY < 0 || fromX < 0) return;
\r
9337 piece = board[fromY][fromX];
\r
9338 if (piece >= EmptySquare) return;
\r
9340 ScreenSquare(fromX, fromY, &start);
\r
9341 ScreenSquare(toX, toY, &finish);
\r
9343 /* All pieces except knights move in straight line */
\r
9344 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9345 mid.x = start.x + (finish.x - start.x) / 2;
\r
9346 mid.y = start.y + (finish.y - start.y) / 2;
\r
9348 /* Knight: make diagonal movement then straight */
\r
9349 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9350 mid.x = start.x + (finish.x - start.x) / 2;
\r
9354 mid.y = start.y + (finish.y - start.y) / 2;
\r
9358 /* Don't use as many frames for very short moves */
\r
9359 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9360 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9362 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9364 animInfo.from.x = fromX;
\r
9365 animInfo.from.y = fromY;
\r
9366 animInfo.to.x = toX;
\r
9367 animInfo.to.y = toY;
\r
9368 animInfo.lastpos = start;
\r
9369 animInfo.piece = piece;
\r
9370 for (n = 0; n < nFrames; n++) {
\r
9371 animInfo.pos = frames[n];
\r
9372 DrawPosition(FALSE, NULL);
\r
9373 animInfo.lastpos = animInfo.pos;
\r
9374 Sleep(appData.animSpeed);
\r
9376 animInfo.pos = finish;
\r
9377 DrawPosition(FALSE, NULL);
\r
9378 animInfo.piece = EmptySquare;
\r
9379 if(gameInfo.variant == VariantAtomic &&
\r
9380 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9381 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9384 /* Convert board position to corner of screen rect and color */
\r
9387 ScreenSquare(column, row, pt)
\r
9388 int column; int row; POINT * pt;
\r
9391 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9392 pt->y = lineGap + row * (squareSize + lineGap);
\r
9394 pt->x = lineGap + column * (squareSize + lineGap);
\r
9395 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9399 /* Generate a series of frame coords from start->mid->finish.
\r
9400 The movement rate doubles until the half way point is
\r
9401 reached, then halves back down to the final destination,
\r
9402 which gives a nice slow in/out effect. The algorithmn
\r
9403 may seem to generate too many intermediates for short
\r
9404 moves, but remember that the purpose is to attract the
\r
9405 viewers attention to the piece about to be moved and
\r
9406 then to where it ends up. Too few frames would be less
\r
9410 Tween(start, mid, finish, factor, frames, nFrames)
\r
9411 POINT * start; POINT * mid;
\r
9412 POINT * finish; int factor;
\r
9413 POINT frames[]; int * nFrames;
\r
9415 int n, fraction = 1, count = 0;
\r
9417 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9418 for (n = 0; n < factor; n++)
\r
9420 for (n = 0; n < factor; n++) {
\r
9421 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9422 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9424 fraction = fraction / 2;
\r
9428 frames[count] = *mid;
\r
9431 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9433 for (n = 0; n < factor; n++) {
\r
9434 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9435 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9437 fraction = fraction * 2;
\r
9443 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9445 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9447 EvalGraphSet( first, last, current, pvInfoList );
\r